//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

#if defined(OEM1_DRIVER)
#define DELAYLD_NO_GETPROCFROMDLL
#include "precomp.h"
#include <linklist.h>
#include "simmgr.h"
#include "rilndisoem1.h"
#include <Delayld.h>


HINSTANCE g_hinstOEMNDIS /*= NULL*/;

#if defined(ENFORA_DRIVER)
#define OEM_LIBRARY TEXT("mux07_10")
#define ATCMD_DLCI	1
#define PKT1_DLCI	8
#define PKT2_DLCI	9
#define PKT3_DLCI	10
#else
#define OEM_LIBRARY TEXT("csmi")
#endif
#define OEM_NdisInitialize TEXT("NdisInitialize")
#define OEM_NdisShutdown   TEXT("NdisShutdown")
#define MAX_IP_ADDRESS_LENGTH 4
typedef HRESULT  (*PFN_NDIS_INITIALIZE)(POEMNDISINITIALIZE);
typedef HRESULT  (*PFN_NDIS_SHUTDOWN)(void);

PFN_NDIS_INITIALIZE 	 g_pfnNdisInitialize;
PFN_NDIS_SHUTDOWN		 g_pfnNdisShutdown;

HINSTANCE LoadPacketLib()
{
	if (!g_hinstOEMNDIS)
		{
		g_hinstOEMNDIS = LoadLibrary(OEM_LIBRARY);
		}
	return g_hinstOEMNDIS;
}

BOOL WaitForCloseResponse(void)
{
	return TRUE;
}

HRESULT GetPacketInterface()
{
	HRESULT Result = E_FAIL;

	HINSTANCE hinst;
	hinst = LoadPacketLib();
	if (hinst)
		{
		g_pfnNdisInitialize =	 (PFN_NDIS_INITIALIZE)GetProcAddress(hinst, OEM_NdisInitialize );
		g_pfnNdisShutdown = 	 (PFN_NDIS_SHUTDOWN)GetProcAddress(hinst, OEM_NdisShutdown );
		if (g_pfnNdisInitialize && g_pfnNdisShutdown )
			{
			Result = S_OK;
			}
		}
	RETAILMSG(MSG_ON, (TEXT("RILNDIS: GetPacketInterface LoadLib = %x Initialize = %x Shutdown = %x\r\n"), hinst, g_pfnNdisInitialize, g_pfnNdisShutdown));
	return Result;
}

DWORD WINAPI NdisNotifyThreadProc(LPVOID lpParameter);

// **************************************************************************
// Function Name:  NdisNotifyThreadProc
// 
// Purpose: 	   Process NDIS events from radio.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
DWORD WINAPI NdisNotifyThreadProc(LPVOID lpParameter)
{
	CRilHandle* pRilDevice = (CRilHandle*)lpParameter;
	DWORD dwResult;

	dwResult = pRilDevice->CRilNdis.NdisNotifyThread();

	return dwResult;
}
#if defined DEBUG

void DumpNdisChannelEntry( POEMNDISCHANNELS pOemNdisChannel )
{
	RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisChannelEntry = %x \r\n"), pOemNdisChannel ));

	if ( pOemNdisChannel )
		{
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry ContextID = %x \r\n"),pOemNdisChannel->ContextID));
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry pCRilInstance = %x \r\n"),pOemNdisChannel->pCRilInstance));
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry pCPdpContext = %x \r\n"),pOemNdisChannel->pCPdpContext));
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry RilNdisGprsContext cbSize = %x \r\n"),pOemNdisChannel->RilNdisGprsContext.cbSize));
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry RilNdisGprsContext dwParams = %x \r\n"),pOemNdisChannel->RilNdisGprsContext.dwParams));
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry RilNdisGprsContext dwContextID = %x \r\n"),pOemNdisChannel->RilNdisGprsContext.dwContextID));
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry RilNdisGprsContext fContextActivation = %x \r\n"),pOemNdisChannel->RilNdisGprsContext.fContextActivation));
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry RilNdisGprsContext tszUserName = %s \r\n"),pOemNdisChannel->RilNdisGprsContext.tszUserName));
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry RilNdisGprsContext tszPassword = %s \r\n"),pOemNdisChannel->RilNdisGprsContext.tszPassword));
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry RilNdisGprsContext tszDNS1 = %s \r\n"),pOemNdisChannel->RilNdisGprsContext.tszDNS1));
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry RilNdisGprsContext tszDNS2 = %s \r\n"),pOemNdisChannel->RilNdisGprsContext.tszDNS2));
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry RilNdisGprsContext pfnNdisReceivePacket = %x \r\n"),pOemNdisChannel->RilNdisGprsContext.pfnNdisReceive));
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry RilNdisGprsContext pfnNdisTransmitDone = %x \r\n"),pOemNdisChannel->RilNdisGprsContext.pfnNdisTransmit));
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry RilNdisGprsContext pfnNdisStatus = %x \r\n"),pOemNdisChannel->RilNdisGprsContext.pfnNdisStatus));
		DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisChannelEntry RilNdisGprsContext pCallbackContext = %x \r\n"),pOemNdisChannel->RilNdisGprsContext.pCallbackContext));

		}

}

void DumpNdisIPConfig( RILNDISIPCONFIG* pRilNdisIpConfig )
{
	if ( pRilNdisIpConfig )
		{
		RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig cbSize = %x \r\n"),pRilNdisIpConfig->cbSize));
		RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig dwContextId = %x \r\n"),pRilNdisIpConfig->dwContextId));
		RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig dwProtocol = %x \r\n"),pRilNdisIpConfig->dwProtocol));
		RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.dwFlags = %x \r\n"),pRilNdisIpConfig->ipv4.dwFlags));
		RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inIPAddress = %x \r\n"),pRilNdisIpConfig->ipv4.inIPAddress));
		RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inPrimaryDNS = %x \r\n"),pRilNdisIpConfig->ipv4.inPrimaryDNS));
		RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inSecondaryDNS = %x \r\n"),pRilNdisIpConfig->ipv4.inSecondaryDNS));
		RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inDefaultGateway = %x \r\n"),pRilNdisIpConfig->ipv4.inDefaultGateway));
		RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inSubnetMask = %x \r\n"),pRilNdisIpConfig->ipv4.inSubnetMask));

		}
}

void DEBUG_TYPE( OEMNDISEVENTTYPE type)
{
	switch ( type )
		{
		case OEM_NDIS_OPEN_COMPLETE:	/* RX packet complete - buffer can be reused   */
			RETAILMSG(MSG_ON, (TEXT("RILNDIS: OEM_NDIS_OPEN_COMPLETE\r\n")));
			break;
		case OEM_NDIS_CLOSE_COMPLETE:	 /* RX packet complete - buffer can be reused	*/
			RETAILMSG(MSG_ON, (TEXT("RILNDIS: OEM_NDIS_CLOSE_COMPLETE\r\n")));
			break;
		}
}

#else
#define DumpNdisChannelEntry(a)
#define DEBUG_TYPE(a)
#define DumpNdisIPConfig(a)
#endif

// **************************************************************************
// Function Name:  myinet_addr
// 
// Purpose: Convert string IP address into a DWORD
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************

DWORD myinet_addr(const UCHAR *lpString, UCHAR length)
{
  DWORD dwIpAddr = 0;
  
	if ( length > MAX_IP_ADDRESS_LENGTH  )
		{
		length = MAX_IP_ADDRESS_LENGTH;
		}
	 
	for(int i=0; i<length; i++)
		{
		if ( i )
			{
			dwIpAddr <<= 8;
			}
		dwIpAddr |= lpString[i];
		}
	
	return dwIpAddr;
}

// **************************************************************************
// Function Name:  myinet_ipv6addr
//
// Purpose: Convert string IPV6 address into a RILNDISIPV6ADDR
//
// Arguments:
//
// Return Values: RILNDISIPV6ADDR struct
//
// Side effects:
//
// Description:
// **************************************************************************
RILNDISIPV6ADDR myinet_ipv6addr(const UCHAR *lpString, UCHAR length)
{
	RILNDISIPV6ADDR ipv6addr;

	memset( &ipv6addr, 0, sizeof(ipv6addr) );

	if ( length == IPV6_ADDRESS_LENGTH )
		{
		memcpy( ipv6addr.Byte, lpString, (IPV6_ADDRESS_LENGTH_IN_UCHAR*sizeof(UCHAR)) );
		}

	return ipv6addr;
}


CRilNDIS::CRilNDIS()
{
	m_pCRilHandle = NULL;
	m_RxMsgQueue = NULL;
	m_RxMsgQueueWrite = NULL;
	m_pNdisChannels = NULL;
	m_NumNdisChannels = 0;
	m_NdisNotifyThread = NULL;
}

CRilNDIS::~CRilNDIS()
{
	m_pCRilHandle = NULL;
}

void CRilNDIS::Init(PVOID pHandle)
{
	m_pCRilHandle = pHandle;
}

BOOL CRilNDIS::NdisStart ( HANDLE CancelEvent )
{
	m_NdisNotifyThread = NULL;
	m_NdisNotifyThreadID = 0;
	m_CancelEvent = CancelEvent;
	m_NdisNotifyThread = CreateThread(NULL, 0, NdisNotifyThreadProc, (LPVOID)m_pCRilHandle, 0, &m_NdisNotifyThreadID);
	if ( m_NdisNotifyThread )
		{
		InitializeCriticalSection(&m_NdisEventList);
		InitializeCriticalSection(&m_ConnectionList);
		InitializeCriticalSection(&m_PdpContextCriticalSection);
		InitializeListHead(&m_PdpContextPendingList);
		InitializeListHead(&m_PdpContextFreeList);
		}

	if ( FAILED(GetPacketInterface()))
		{
		RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisStart GetPacketInterface FAILED \r\n")));
		}

	return (m_NdisNotifyThread ? TRUE : FALSE );
}

void CRilNDIS::NdisStop( void )
{
	if ( m_NdisNotifyThread )
		{
		(void)WaitForSingleObject(m_NdisNotifyThread,10000);
		(void)CloseHandle(m_NdisNotifyThread);
		m_NdisNotifyThread = NULL;
		m_NdisNotifyThreadID = 0;
		DeleteCriticalSection(&m_NdisEventList);
		DeleteCriticalSection(&m_ConnectionList);
		DeleteCriticalSection(&m_PdpContextCriticalSection);
		}
}

HRESULT
CRilNDIS::NdisGetFreeEntry ( DWORD *Index )
{
	HRESULT Result = E_FAIL;
	EnterCriticalSection(&m_ConnectionList);
	for ( DWORD IndexCount=0; IndexCount<m_NumNdisChannels; IndexCount++)
		{
		DumpNdisChannelEntry(&m_pNdisChannels[IndexCount]);
		if ( 0 == m_pNdisChannels[IndexCount].ContextID )
			{
			Result = S_OK;
			*Index = IndexCount;
			break;
			}
		}
	LeaveCriticalSection(&m_ConnectionList);
	return Result;
}

void	
CRilNDIS::NdisRemoveNdisEntry ( DWORD ContextID )
{
	EnterCriticalSection(&m_ConnectionList);
	for(DWORD i=0;i<m_NumNdisChannels;i++)
		{
		if ( ContextID && (m_pNdisChannels[i].ContextID == ContextID) ) 
			{
			if ( m_pNdisChannels[i].pCPdpContext )
				{
				delete (CPdpContext*)m_pNdisChannels[i].pCPdpContext;
				}
			m_NdisAsyncCommandList.RemoveAllCommands( ContextID );
			memset( &m_pNdisChannels[i], 0, sizeof(OEMNDISCHANNELS)); 
			break;
			}
		}
	LeaveCriticalSection(&m_ConnectionList);			
}

void	
CRilNDIS::NdisAddNdisEntry ( DWORD ContextID, DWORD Index, PVOID pCRilInstance, PVOID pCPdpContext, const RILNDISGPRSCONTEXT *lpRilNdisGprsContext )
{	 
	RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisAddNdisEntry cid = %x, index = %x, pCrilInstance = %x pCPdpContext = %x \r\n"), ContextID, Index, pCRilInstance, pCPdpContext));
	EnterCriticalSection(&m_ConnectionList);
	ASSERT((Index >= 0) && (Index < m_NumNdisChannels));
	if ( pCRilInstance )
		{
		m_pNdisChannels[Index].ContextID= ContextID;
		m_pNdisChannels[Index].pCRilInstance = pCRilInstance;
		m_pNdisChannels[Index].pCPdpContext = pCPdpContext;
		CeSafeCopyMemory(&m_pNdisChannels[Index].RilNdisGprsContext, lpRilNdisGprsContext, sizeof(m_pNdisChannels[Index].RilNdisGprsContext));
		DumpNdisChannelEntry(&m_pNdisChannels[Index]);
		}
	LeaveCriticalSection(&m_ConnectionList);
}

PVOID
CRilNDIS::NdisPdpContextFromCid( DWORD ContextID  )
{
	PVOID pReturn = NULL;
	EnterCriticalSection(&m_ConnectionList);
	for(DWORD i=0;i<m_NumNdisChannels;i++)
		{
		if ( ContextID && (m_pNdisChannels[i].ContextID== ContextID) )
			{
			pReturn = m_pNdisChannels[i].pCPdpContext;
			break;
			}
		}
	LeaveCriticalSection(&m_ConnectionList);   
	return pReturn;
}
// **************************************************************************
// Function Name: NdisRilInstanceFromHandle
// 
// Purpose: 	  Search of the NDIS channel handle associated with a cid.
//
// Arguments:	  IN cid, OUT hNdisHandle
//
// Return Values: E_FAIL if not found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
VOID* CRilNDIS::NdisRilInstanceFromCID( DWORD ContextID )
{
	PVOID pReturn = NULL;
	EnterCriticalSection(&m_ConnectionList);
	for(DWORD i=0;i<m_NumNdisChannels;i++)
		{
		if ( ContextID && (m_pNdisChannels[i].ContextID == ContextID) )
			{
			pReturn = m_pNdisChannels[i].pCRilInstance;
			break;
			}
		}
	LeaveCriticalSection(&m_ConnectionList);   
	return pReturn;
}

// **************************************************************************
// Function Name: NdisRilCallbackFromCID
// 
// Purpose: 	  Search of the NDIS channel handle associated with a cid.
//
// Arguments:	  IN cid, OUT hNdisHandle
//
// Return Values: E_FAIL if not found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void CRilNDIS::NdisRilCallbacksFromCID( DWORD ContextID, PVOID *pCallbackContext, RILNDISRECEIVECALLBACK *pfnRilReceiveCallback, RILNDISTRANSMITCALLBACK *pfnRilTransmitCallback, RILNDISSTATUSCALLBACK *pfnRilStatusCallback)
{
	EnterCriticalSection(&m_ConnectionList);
	*pfnRilReceiveCallback = NULL;
	*pfnRilTransmitCallback = NULL;
	*pfnRilStatusCallback = NULL;
	*pCallbackContext = NULL;
	for(DWORD i=0;i<m_NumNdisChannels;i++)
		{
		if ( ContextID && (m_pNdisChannels[i].ContextID == ContextID) )
			{
			*pfnRilReceiveCallback = m_pNdisChannels[i].RilNdisGprsContext.pfnNdisReceive;
			*pfnRilTransmitCallback = m_pNdisChannels[i].RilNdisGprsContext.pfnNdisTransmit;
			*pfnRilStatusCallback = m_pNdisChannels[i].RilNdisGprsContext.pfnNdisStatus;
			*pCallbackContext = m_pNdisChannels[i].RilNdisGprsContext.pCallbackContext;
			break;
			}
		}
	LeaveCriticalSection(&m_ConnectionList);   
}

// **************************************************************************
// Function Name: NdisRilGprsContextFromCID
// 
// Purpose: 	  Search of the NDIS channel handle associated with a cid.
//
// Arguments:	  IN cid, OUT LPRILNDISGPRSCONTEXT
//
// Return Values: E_FAIL if not found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
BOOL CRilNDIS::NdisRilGprsContextFromCID( DWORD ContextID, LPRILNDISGPRSCONTEXT pRilGprsContext)
{
    BOOL fReturn = FALSE;
	EnterCriticalSection(&m_ConnectionList);
    if ( pRilGprsContext )
    {
    	for(DWORD i=0;i<m_NumNdisChannels;i++)
    		{
    		if ( ContextID && (m_pNdisChannels[i].ContextID == ContextID) )
    			{
		        CeSafeCopyMemory(pRilGprsContext, &m_pNdisChannels[i].RilNdisGprsContext, sizeof(m_pNdisChannels[i].RilNdisGprsContext));
                fReturn = TRUE;
    			break;
    			}
    		}
    }
	LeaveCriticalSection(&m_ConnectionList); 
    return fReturn;  
}

// **************************************************************************
// Function Name: NdisRemoveCallbacks
// 
// Purpose: 	  
//
// Arguments:	  
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void	CRilNDIS::NdisRemoveCallbacks ( DWORD ContextID )
{	 
	RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisRemoveCallbacks cid = %x  \r\n"), ContextID));
	EnterCriticalSection(&m_ConnectionList);
	for(DWORD i=0;i<m_NumNdisChannels;i++)
	{
		if ( ContextID && (m_pNdisChannels[i].ContextID == ContextID) )
		{
			m_pNdisChannels[i].RilNdisGprsContext.pCallbackContext = NULL;
			m_pNdisChannels[i].RilNdisGprsContext.pfnNdisReceive= NULL;
			m_pNdisChannels[i].RilNdisGprsContext.pfnNdisStatus = NULL;
			m_pNdisChannels[i].RilNdisGprsContext.pfnNdisTransmit= NULL;
			break;
		}
	}
	LeaveCriticalSection(&m_ConnectionList);
}

// **************************************************************************
// Function Name:  NdisCloseAllConnections
// 
// Purpose: 	   Close all NDIS connections
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void   CRilNDIS::NdisCloseAllConnections ( void )
{
	EnterCriticalSection(&m_ConnectionList);
	for(DWORD i=0;i<m_NumNdisChannels;i++)
		{
		if ( m_pNdisChannels[i].ContextID)
			{
			RemoveAndSignalAllPdpContextEntries(m_pNdisChannels[i].ContextID);
			NdisShutdownConnection(m_pNdisChannels[i].ContextID);
			}
		}
	LeaveCriticalSection(&m_ConnectionList);
}

// **************************************************************************
// Function Name:	NdisShutdownConnection
// 
// Purpose: 		Shutdown and NDIS connection and cleanup related items.
//
// Arguments:		IN handle to channel to close.
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void CRilNDIS:: NdisShutdownConnection ( DWORD ContextID )
{
	if ( ContextID )
		{
		// Notify it's down
		RILGPRSCONTEXTACTIVATED RilGprsContexActivated;
		RilGprsContexActivated.cbSize = sizeof(RILGPRSCONTEXTACTIVATED);
		RilGprsContexActivated.fActivated = FALSE;
		RilGprsContexActivated.dwContextID = ContextID;
		RilGprsContexActivated.dwEvent = RIL_RILGPRSCONTEXTACTIVATED_MEDEACT;
		((CRilHandle*)m_pCRilHandle)->BroadcastRealBlobNotification(RIL_NOTIFY_GPRSCONNECTIONSTATUS, &RilGprsContexActivated, sizeof(RILGPRSCONTEXTACTIVATED));
		// Remove the entry from our list of Ndis handles/Cids
		NdisRemoveNdisEntry(ContextID);
		}
}

// **************************************************************************
// Function Name: NdisNotifyThread
// 
// Purpose: 	  Thread running to process NDIS events.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
DWORD	CRilNDIS:: NdisNotifyThread(void)
{
	DWORD				Triggered, Index;
	OEMNDISAPIREFERENCE OemNdisApiReference;
	OEMNDISEVENT		OemNdisEvent;
	CRilInstanceHandle *pCRilInstance = NULL;
	HANDLE				NdisThreadEvents[MAX_OEMNDIS_EVENTS];
	MSGQUEUEOPTIONS 	MsgQueueOptions;
	DWORD				RxQueueBytesRead;
	DWORD				RxQueueReadFlags;
	HRESULT				Result = E_FAIL;
	OEMNDISINITIALIZE	OemNdisInitialize;
	CPdpContext			*pCPdpContext = NULL;
	RILGPRSCONTEXTACTIVATED RilGprsContexActivated;



	// Tell the main thread that we've reached the checkpoint
	((CRilHandle*)m_pCRilHandle)->GetCheckPoint()->Reached();
 
	/* Setup our Rx Message Queue */   
	MsgQueueOptions.dwSize				   = sizeof(MSGQUEUEOPTIONS);
	MsgQueueOptions.dwFlags 			   = 0;
	MsgQueueOptions.dwMaxMessages		   = 0;
	MsgQueueOptions.cbMaxMessage		   = sizeof(OEMNDISEVENT);
	MsgQueueOptions.bReadAccess 		   = TRUE;

	m_RxMsgQueue = CreateMsgQueue(NULL,&MsgQueueOptions);
	ASSERT(m_RxMsgQueue);
	if ( NULL == m_RxMsgQueue )
		{
		goto Exit;
		}

	/* Setup the handle to write to the Rx Message Queue */
	MsgQueueOptions.bReadAccess 		   = FALSE;
	m_RxMsgQueueWrite = OpenMsgQueue(GetCurrentProcess(),m_RxMsgQueue, &MsgQueueOptions);
	ASSERT ( m_RxMsgQueueWrite );
	if ( NULL == m_RxMsgQueueWrite )
		{
		goto Exit;
		}
	/* Setup Rx Message Queue Done */  
   
	NdisThreadEvents[NDIS_STOP_THREAD_INDEX] = m_CancelEvent;
	NdisThreadEvents[NDIS_RX_QUEUE_INDEX] = m_RxMsgQueue;


	m_NumNdisChannels = 3;
	m_pNdisChannels = (POEMNDISCHANNELS) LocalAlloc ( LPTR, m_NumNdisChannels*sizeof(OEMNDISCHANNELS));
	if ( NULL == m_pNdisChannels )
		{
		goto Exit;
		}

	memset(m_pNdisChannels, 0, m_NumNdisChannels * sizeof(OEMNDISCHANNELS));
	
	for(DWORD DebugChannelIndex = 0; DebugChannelIndex < m_NumNdisChannels; DebugChannelIndex++ )
		{
		DumpNdisChannelEntry(&m_pNdisChannels[DebugChannelIndex]);
		}

	
	do 
		{
		Triggered = WaitForMultipleObjects( MAX_OEMNDIS_EVENTS, NdisThreadEvents, FALSE,  INFINITE );
		Index = Triggered - WAIT_OBJECT_0;
		
		if ( Index == NDIS_STOP_THREAD_INDEX )
			{
			NdisCloseAllConnections();
			break;
			}

		// The notification is coming from the RADIO/Context Control
		if( Index == NDIS_RX_QUEUE_INDEX ) 
			{
			while (ReadMsgQueue(
					m_RxMsgQueue,
					&OemNdisEvent,
					sizeof(OemNdisEvent),
					&RxQueueBytesRead,
					1,
					&RxQueueReadFlags))
				{
				if ( RxQueueBytesRead == sizeof(OemNdisEvent))
					{
					DEBUG_TYPE(OemNdisEvent.Type);
					memset(&OemNdisApiReference,0,sizeof(OemNdisApiReference)); 
			   
					switch ( OemNdisEvent.Type )
						{

						case OEM_NDIS_OPEN_COMPLETE: 
                        memset(&OemNdisApiReference, 0, sizeof(OemNdisApiReference));
						OemNdisApiReference.EventType= OEM_NDIS_OPEN_COMPLETE;
						OemNdisApiReference.ContextID = OemNdisEvent.ContextID;
                        OemNdisApiReference.RilNdisGprsContextResponse.dwError = OemNdisEvent.Status;
                        OemNdisApiReference.RilNdisGprsContextResponse.cbSize = sizeof(OemNdisApiReference.RilNdisGprsContextResponse);
                        OemNdisApiReference.RilNdisGprsContextResponse.dwContextID = OemNdisEvent.ContextID;
                        if ( 0 == OemNdisEvent.Status )
                        {
                            OemNdisEvent.Status = 1; //default error condition.
                            if ( TRUE == NdisRilGprsContextFromCID( OemNdisEvent.ContextID, &OemNdisInitialize.RilNdisGprsContext))
                            {
        						if (  g_pfnNdisInitialize )
        						{        
        							OemNdisInitialize.Size = sizeof(OemNdisInitialize);
        							OemNdisInitialize.dwType = OEM_NDIS_INITIALIZE_INIT;
        							Result = g_pfnNdisInitialize ( &OemNdisInitialize );
                                    if ( S_OK == Result )
                                    {
                                        OemNdisEvent.Status = 0;
    		                            CeSafeCopyMemory(&OemNdisApiReference.RilNdisGprsContextResponse, &OemNdisInitialize.RilNdisGprsContextResponse,sizeof(OemNdisApiReference.RilNdisGprsContextResponse));
                						NdisConvertConfig ( &OemNdisEvent.IpConfig, &OemNdisApiReference.RilNdisGprsContextResponse.RilNdisIPConfig);
                						OemNdisApiReference.RilNdisGprsContextResponse.RilNdisIPConfig.dwContextId = OemNdisEvent.ContextID;
                						DumpNdisIPConfig( &OemNdisApiReference.RilNdisGprsContextResponse.RilNdisIPConfig );
                                        OemNdisApiReference.RilNdisGprsContextResponse.dwParams = RIL_PARAM_RILNDISGPRSCONTEXTRESPONSE_IPCONFIG | RIL_PARAM_RILNDISGPRSCONTEXTRESPONSE_FUNCTIONS;
                                    }
        						}
                            }
                        }

                        OemNdisApiReference.Status = OemNdisEvent.Status;
						m_NdisAsyncCommandList.ProcessCommand ( &OemNdisApiReference ); 
						if ( OemNdisEvent.Status ) // FAILURE!
							{
							pCPdpContext = (CPdpContext*)NdisPdpContextFromCid( OemNdisEvent.ContextID );
							ASSERT(pCPdpContext);
							pCPdpContext->CloseContext(OemNdisEvent.ContextID);
							NdisShutdownConnection ( OemNdisEvent.ContextID);
							}
						else
							{	
							RilGprsContexActivated.cbSize = sizeof(RILGPRSCONTEXTACTIVATED);
							RilGprsContexActivated.fActivated = TRUE;
							RilGprsContexActivated.dwContextID = OemNdisEvent.ContextID;
							RilGprsContexActivated.dwEvent = RIL_RILGPRSCONTEXTACTIVATED_MEACT;
							((CRilHandle*)m_pCRilHandle)->BroadcastRealBlobNotification(RIL_NOTIFY_GPRSCONNECTIONSTATUS, &RilGprsContexActivated, sizeof(RILGPRSCONTEXTACTIVATED));
							}
						ProcessNextContextRequest();
						break;
					
						case OEM_NDIS_CLOSE_COMPLETE: 
                        memset(&OemNdisApiReference, 0, sizeof(OemNdisApiReference));
						OemNdisApiReference.Status = OemNdisEvent.Status;
						OemNdisApiReference.EventType= OEM_NDIS_CLOSE_COMPLETE;
						OemNdisApiReference.ContextID = OemNdisEvent.ContextID;
                        OemNdisApiReference.RilNdisGprsContextResponse.dwError = OemNdisEvent.Status;
                        OemNdisApiReference.RilNdisGprsContextResponse.cbSize = sizeof(OemNdisApiReference.RilNdisGprsContextResponse);
                        OemNdisApiReference.RilNdisGprsContextResponse.dwContextID = OemNdisEvent.ContextID;
						m_NdisAsyncCommandList.ProcessCommand ( &OemNdisApiReference );
						NdisShutdownConnection ( OemNdisEvent.ContextID );

						if (  g_pfnNdisInitialize )
							{
							memset(&OemNdisInitialize, 0, sizeof(OemNdisInitialize));

							// Reset the context count.
							OemNdisInitialize.Size = sizeof(OemNdisInitialize);
							OemNdisInitialize.dwType = OEM_NDIS_INITIALIZE_CLOSE;
							OemNdisInitialize.RilNdisGprsContext.dwContextID = OemNdisEvent.ContextID;
							Result = g_pfnNdisInitialize ( &OemNdisInitialize );
							}

						ProcessNextContextRequest();
						break;
						}
				   }
				}
				continue;
			}
		else
			{
			ASSERT(0);
			}
		
		}while(1);

Exit:
	CloseMsgQueue (m_RxMsgQueue);
	CloseMsgQueue (m_RxMsgQueueWrite);
	if ( m_pNdisChannels )
		{
		LocalFree(m_pNdisChannels);
		}
	
	return 0;
}


// **************************************************************************
// Function Name:	NdisAllocateCommand
// 
// Purpose:    Allocate an asynchronous command. This method is used
//			   when the response will key off of the event type.
//
// Arguments:  IN eType: expected event.
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void CRilNDIS::NdisAllocateCommand (  OEMNDISEVENTTYPE Type, DWORD ContextID, LPVOID pCRilInstance, HRESULT CommandID )
{
	if ( ContextID && pCRilInstance )
		{
		OEMNDISAPIREFERENCE OemNdisApiReference;
		memset(&OemNdisApiReference,0,sizeof(OemNdisApiReference));
	
		OemNdisApiReference.pCRilInstance = pCRilInstance;
		OemNdisApiReference.EventType = Type;
		OemNdisApiReference.ContextID = ContextID;
		OemNdisApiReference.CommandID = CommandID; 
		m_NdisAsyncCommandList.AddCommand ( &OemNdisApiReference );
		}	
}

void 
CRilNDIS:: NdisRemoveCommand ( HRESULT hrCommandID )
{
	if ( hrCommandID )
		{
		m_NdisAsyncCommandList.RemoveCommand ( hrCommandID );
		}
}

void CRilNDIS::NdisConvertConfig ( const OEMNDISIPCONFIG* pOemNdisConfig,  RILNDISIPCONFIG* pRilNdisIpConfig)
{
	if ( pOemNdisConfig && pRilNdisIpConfig )
		{
		memset( pRilNdisIpConfig, 0, sizeof(RILNDISIPCONFIG));
		pRilNdisIpConfig->cbSize = sizeof(RILNDISIPCONFIG);

		/* derive whether address is IPv4(default) or IPv6 */
		pRilNdisIpConfig->dwProtocol = RIL_PARAM_NDISIPCONFIG_PROTOCOL_IPV4;
		if ( pOemNdisConfig->ipAddress.length == IPV6_ADDRESS_LENGTH )
			{
			pRilNdisIpConfig->dwProtocol = RIL_PARAM_NDISIPCONFIG_PROTOCOL_IPV6;
			}

		/* fill in protocol specific parameters */
		if ( pRilNdisIpConfig->dwProtocol == RIL_PARAM_NDISIPCONFIG_PROTOCOL_IPV4 )
			{
			pRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_SUBNETMASK;
			pRilNdisIpConfig->ipv4.inSubnetMask = 0xffffff00;

			if ( pOemNdisConfig->ipAddress.length && pOemNdisConfig->ipAddress.address )
				{
				pRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPADDR;
				pRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_DEFAULTGATEWAY;
				pRilNdisIpConfig->ipv4.inIPAddress = myinet_addr( pOemNdisConfig->ipAddress.address, pOemNdisConfig->ipAddress.length );
				pRilNdisIpConfig->ipv4.inDefaultGateway = pRilNdisIpConfig->ipv4.inIPAddress ^ 0xFF;
				}

			if ( pOemNdisConfig->primaryDnsAddress.length && pOemNdisConfig->primaryDnsAddress.address)
				{
				pRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_PRIMARYDNS;
				pRilNdisIpConfig->ipv4.inPrimaryDNS = myinet_addr( pOemNdisConfig->primaryDnsAddress.address, pOemNdisConfig->primaryDnsAddress.length );
				}

			if ( pOemNdisConfig->secondaryDnsAddress.length && pOemNdisConfig->secondaryDnsAddress.address)
				{
				pRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_SECONDARYDNS;
				pRilNdisIpConfig->ipv4.inSecondaryDNS = myinet_addr( pOemNdisConfig->secondaryDnsAddress.address, pOemNdisConfig->secondaryDnsAddress.length );
				}
			}
		else if ( pRilNdisIpConfig->dwProtocol == RIL_PARAM_NDISIPCONFIG_PROTOCOL_IPV6 )
			{
			if ( pOemNdisConfig->ipAddress.length )
				{
				pRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_IPADDR;
				pRilNdisIpConfig->ipv6.inIPAddress = myinet_ipv6addr( pOemNdisConfig->ipAddress.address, pOemNdisConfig->ipAddress.length );

				UCHAR subnetmask[IPV6_ADDRESS_LENGTH];
				memset( subnetmask, 0xFF, sizeof(subnetmask) );
				pRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_SUBNETMASK;
				pRilNdisIpConfig->ipv6.inSubnetMask = myinet_ipv6addr( subnetmask, IPV6_ADDRESS_LENGTH );

				pRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_DEFAULTGATEWAY;
				pRilNdisIpConfig->ipv6.inDefaultGateway = pRilNdisIpConfig->ipv6.inIPAddress;
				}

			if ( pOemNdisConfig->primaryDnsAddress.length )
				{
				pRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_PRIMARYDNS;
				pRilNdisIpConfig->ipv6.inPrimaryDNS = myinet_ipv6addr( pOemNdisConfig->primaryDnsAddress.address, pOemNdisConfig->primaryDnsAddress.length );
				}

			if ( pOemNdisConfig->secondaryDnsAddress.length )
				{
				pRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_SECONDARYDNS;
				pRilNdisIpConfig->ipv6.inSecondaryDNS = myinet_ipv6addr( pOemNdisConfig->secondaryDnsAddress.address, pOemNdisConfig->secondaryDnsAddress.length );
				}
			}
		}
}

inline void
CRilNDIS::PdpContextLock()
{
	EnterCriticalSection(&m_PdpContextCriticalSection);
}

inline void
CRilNDIS::PdpContextUnlock()
{
	LeaveCriticalSection(&m_PdpContextCriticalSection);
}

inline BOOL
CRilNDIS::IsPdpContextLocked()
{
	return m_PdpContextCriticalSection.OwnerThread == (HANDLE)GetCurrentThreadId();
}

PPDP_CONTEXT_ENTRY
CRilNDIS::PdpContextEntryGet()
{
	PPDP_CONTEXT_ENTRY pEntry = NULL;

	ASSERT(IsPdpContextLocked());

	if (!IsListEmpty(&m_PdpContextFreeList))
		{
		pEntry = (PPDP_CONTEXT_ENTRY)RemoveHeadList(&m_PdpContextFreeList);
		}

	if (pEntry == NULL)
		{
		pEntry = (PPDP_CONTEXT_ENTRY)LocalAlloc(LPTR, sizeof(*pEntry));
		}

	if ( pEntry )
		{
		pEntry->AsyncCommandID = 0;
		pEntry->pCRilInstance = NULL;
		memset(&pEntry->RilNdisGprsContext, 0, sizeof(pEntry->RilNdisGprsContext));
		}

	return pEntry;
}

void 
CRilNDIS::DumpPdpContextList ( void )
{

	PPDP_CONTEXT_ENTRY pEntry;
	PdpContextLock();
	//
	// Find entry with matching ContextID in our list of pending requests
	//

	for (pEntry = (PPDP_CONTEXT_ENTRY)(m_PdpContextPendingList.Flink);
		 TRUE;
		 pEntry = (PPDP_CONTEXT_ENTRY)pEntry->Node.Flink)
		{
		if (pEntry == (PPDP_CONTEXT_ENTRY)(&m_PdpContextPendingList))
			{
			break;
			}
		RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpPdpContextList	pPdpContextEntry = %x \r\n"), pEntry));
		}
	PdpContextUnlock();

}

void 
CRilNDIS::AddPdpContextEntry( const RILNDISGPRSCONTEXT *pRilNdisGprsContext, HRESULT AsyncCommandID, PVOID pCRilInstance )
{
	PPDP_CONTEXT_ENTRY pEntry;
	PdpContextLock();
	pEntry = PdpContextEntryGet();
	ASSERT(pEntry);
	if ( pEntry )
		{
		pEntry->AsyncCommandID = AsyncCommandID;
		pEntry->pCRilInstance = pCRilInstance;
		CeSafeCopyMemory(&pEntry->RilNdisGprsContext,pRilNdisGprsContext,sizeof(pEntry->RilNdisGprsContext));
		InsertTailList(&m_PdpContextPendingList, &pEntry->Node);
		}
	PdpContextUnlock();
	RETAILMSG(MSG_ON, (TEXT("RILNDIS: AddPdpContextEntry	pPdpContextEntry = %x \r\n"), pEntry));
	DumpPdpContextList();
}

void 
CRilNDIS::RemovePdpContextEntry( PPDP_CONTEXT_ENTRY pPdpContextEntry )
{
	PPDP_CONTEXT_ENTRY pEntry;
	PdpContextLock();
	//
	// Find entry with matching ContextID in our list of pending requests
	//

	RETAILMSG(MSG_ON, (TEXT("RILNDIS: RemovePdpContextEntry  pPdpContextEntry = %x \r\n"), pPdpContextEntry));

	for (pEntry = (PPDP_CONTEXT_ENTRY)(m_PdpContextPendingList.Flink);
		 TRUE;
		 pEntry = (PPDP_CONTEXT_ENTRY)pEntry->Node.Flink)
		{
		if (pEntry == (PPDP_CONTEXT_ENTRY)(&m_PdpContextPendingList))
			{
			ASSERT(0);
			break;
			}

		if ( pEntry == pPdpContextEntry )
			{
			RemoveEntryList(&pEntry->Node);
			InsertTailList(&m_PdpContextFreeList, &pEntry->Node);
			break;
			}
		}
	PdpContextUnlock();
	DumpPdpContextList();

}


PPDP_CONTEXT_ENTRY
CRilNDIS::GetNextPdpContextEntry(  void )
{
	PPDP_CONTEXT_ENTRY pEntry;
	PPDP_CONTEXT_ENTRY pReturnEntry = NULL;
	PdpContextLock();
	//
	// Find entry with matching ContextID in our list of pending requests
	//
	for (pEntry = (PPDP_CONTEXT_ENTRY)(m_PdpContextPendingList.Flink);
		 TRUE;
		 pEntry = (PPDP_CONTEXT_ENTRY)pEntry->Node.Flink)
		{
		if (pEntry == (PPDP_CONTEXT_ENTRY)(&m_PdpContextPendingList))
			{
			break;
			}
		else
			{
			pReturnEntry = pEntry;
			break;
			}
		}
	PdpContextUnlock();

	RETAILMSG(MSG_ON, (TEXT("RILNDIS: GetNextPdpContextEntry	pReturnEntry = %x \r\n"), pReturnEntry));
	return pReturnEntry;
}

BOOL			
CRilNDIS::IsPdpContextListEmpty( void )
{
	BOOL fReturn = IsListEmpty(&m_PdpContextPendingList);
	RETAILMSG(MSG_ON, (TEXT("RILNDIS: IsPdpContextListEmpty = %s \r\n"), 
			fReturn ? L"TRUE" : L"FALSE"));

	return fReturn;
}

void 
CRilNDIS::RemoveAndSignalAllPdpContextEntries ( DWORD ContextID )
{
	PPDP_CONTEXT_ENTRY pEntry;
	DWORD ResultDetails;
	BOOL	ExitNow = FALSE;

	PdpContextLock();
	//
	// Find entry with matching ContextID in our list of pending requests
	//
	while ( FALSE == ExitNow )
		{
		for (pEntry = (PPDP_CONTEXT_ENTRY)(m_PdpContextPendingList.Flink);
			 TRUE;
			 pEntry = (PPDP_CONTEXT_ENTRY)pEntry->Node.Flink)
			{
			if (pEntry == (PPDP_CONTEXT_ENTRY)(&m_PdpContextPendingList))
				{
				ExitNow = TRUE;
				break;
				}
			if ( pEntry->RilNdisGprsContext.dwContextID== ContextID )
				{
				ResultDetails = RIL_RESULT_ERROR;
				((CRilInstanceHandle*)pEntry->pCRilInstance)->Notify(RIL_RESULT_ERROR,	pEntry->AsyncCommandID, &ResultDetails, sizeof(ResultDetails));
				RemoveEntryList(&pEntry->Node);
				InsertTailList(&m_PdpContextFreeList, &pEntry->Node);
				break;
				}
			}
		}
	PdpContextUnlock();

}

void			
CRilNDIS::ProcessNextContextRequest(void)
{
	PPDP_CONTEXT_ENTRY	pPdpContextEntry=NULL;
	RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessNextContextRequest \r\n")));
	LockPdpContextList();
	pPdpContextEntry = GetNextPdpContextEntry();
	ASSERT(pPdpContextEntry);
	RemovePdpContextEntry(pPdpContextEntry);
	pPdpContextEntry = GetNextPdpContextEntry();
	while (pPdpContextEntry)
		{ 
		ASSERT(pPdpContextEntry->AsyncCommandID);
		ASSERT(pPdpContextEntry->pCRilInstance);
		HRESULT Result=E_FAIL;	
		DWORD Index;

		RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessNextContextRequest	cid = %x, Activation = %s pCRilInstance = %x AsyncCommandID = %x\r\n"), 
				pPdpContextEntry->RilNdisGprsContext.dwContextID, 
				pPdpContextEntry->RilNdisGprsContext.fContextActivation ? L"TRUE" : L"FALSE", 
				pPdpContextEntry->pCRilInstance,
				pPdpContextEntry->AsyncCommandID));

		CPdpContext	*pCPdpContext = (CPdpContext*)NdisPdpContextFromCid( pPdpContextEntry->RilNdisGprsContext.dwContextID );

	   if ( !pPdpContextEntry->RilNdisGprsContext.fContextActivation )
			{
			if ( pCPdpContext) 
				{
				NdisAllocateCommand ( OEM_NDIS_CLOSE_COMPLETE, pPdpContextEntry->RilNdisGprsContext.dwContextID, (LPVOID)pPdpContextEntry->pCRilInstance, pPdpContextEntry->AsyncCommandID ); 
				Result = pCPdpContext->ActivateContext((PVOID) &pPdpContextEntry->RilNdisGprsContext);
				if ( FAILED(Result))
					{
					RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessNextContextRequest  CLOSE handle = %x Call FAILED\r\n"), pCPdpContext));
					NdisShutdownConnection ( pPdpContextEntry->RilNdisGprsContext.dwContextID );
					}
				else
					{
					RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessNextContextRequest  CLOSE handle = %x \r\n"), pCPdpContext));
					}
				}
			else // No connection object - no connection
				{
				Result = RIL_E_INVALIDCONTEXTSTATE; 
				}
			}
		else 
			{
			if ( NULL == pCPdpContext) // This should normally be the case.
				{ 
				// Check to see if there are any available to initialise.
				if ( SUCCEEDED(NdisGetFreeEntry( &Index)) )
					{
					HANDLE NdisRxMsgQueueWriteHandle = NdisGetRxMsgQueueWriteHandle();
					pCPdpContext = new CPdpContext(NdisRxMsgQueueWriteHandle);
					if ( pCPdpContext )
						{
						NdisAddNdisEntry(pPdpContextEntry->RilNdisGprsContext.dwContextID,	Index, pPdpContextEntry->pCRilInstance, pCPdpContext, &pPdpContextEntry->RilNdisGprsContext ); 
						NdisAllocateCommand ( OEM_NDIS_OPEN_COMPLETE, pPdpContextEntry->RilNdisGprsContext.dwContextID, (LPVOID)pPdpContextEntry->pCRilInstance, pPdpContextEntry->AsyncCommandID);
						Result = pCPdpContext->ActivateContext((PVOID) &pPdpContextEntry->RilNdisGprsContext);
						if ( FAILED(Result))
							{
							RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessNextContextRequest  OPEN FAILED \r\n")));
							NdisRemoveNdisEntry(pPdpContextEntry->RilNdisGprsContext.dwContextID);
							}
						else
							{
							RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessNextContextRequest  OPEN handle = %x \r\n"), pCPdpContext));
							}
						}
					}
				else
					{
					Result = RIL_E_MAXCONTEXTS;
					}
				}  
			else // connection object - connection
				{
				Result = RIL_E_INVALIDCONTEXTSTATE;
				}
			}

			// Process error condition
			if ( FAILED(Result) )
				{
				NdisRemoveCommand( pPdpContextEntry->AsyncCommandID );
				((CRilInstanceHandle*)pPdpContextEntry->pCRilInstance)->Notify(RIL_RESULT_ERROR, 
												pPdpContextEntry->AsyncCommandID, 
												&Result, 
												sizeof(Result));
				RemovePdpContextEntry(pPdpContextEntry);
				pPdpContextEntry = GetNextPdpContextEntry();
				}
			else
				{
				pPdpContextEntry = NULL;
				}

		}
	UnlockPdpContextList();
}




CRilInstanceNDIS::CRilInstanceNDIS()
{
	m_pCrilInstance = NULL;
}

CRilInstanceNDIS::~CRilInstanceNDIS()
{
	m_pCrilInstance = NULL;
}

void CRilInstanceNDIS::Init(PVOID hInstance)
{
	m_pCrilInstance = hInstance;
}

// **************************************************************************
// Function Name: NdisSendPacket
// 
// Purpose: 	  Add async command and make NDIS call.
//
// Arguments:	  IN ndis packet pointer
//
// Return Values:	E_FAIL if parameter or immediate failure detected.
//					Command result value for async response.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT  CRilInstanceNDIS::NdisSendPacket( RILNDISPACKET* lpNdisPacket ) 
{	
   return S_OK;
}



// **************************************************************************
// Function Name: NdisSetGPRSContextActivated
// 
// Purpose: 	 Bring the GRPS context up or down depending on input.
//
// Arguments:	 IN structure defining the connection details.
//
// Return Values:  E_FAIL if error detected.
//				   Command result for async response.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT  CRilInstanceNDIS::NdisSetGPRSContextActivated (const RILNDISGPRSCONTEXT *lpNdisSetGprsContextActivated )
{
	HRESULT Result=E_FAIL; 
	CRilHandle* pCrilHandle = ((CRilInstanceHandle*)m_pCrilInstance)->GetCrilHandle();
	HANDLE NdisRxMsgQueueWriteHandle = pCrilHandle->CRilNdis.NdisGetRxMsgQueueWriteHandle();
	HRESULT AsyncCommandID = 0;
	DWORD Index;


	if ( NdisRxMsgQueueWriteHandle && 
		pCrilHandle && 
		lpNdisSetGprsContextActivated && 
		lpNdisSetGprsContextActivated->dwContextID && 
		lpNdisSetGprsContextActivated->dwContextID <= pCrilHandle->CRilNdis.MaxContextID())
		{
		RILNDISGPRSCONTEXT NdisSetGprsContextActivated;
		CeSafeCopyMemory(&NdisSetGprsContextActivated, lpNdisSetGprsContextActivated, sizeof(NdisSetGprsContextActivated));

		if ( (TRUE == NdisSetGprsContextActivated.fContextActivation &&
			 NdisSetGprsContextActivated.pfnNdisReceive &&
			 NdisSetGprsContextActivated.pfnNdisTransmit &&
			 NdisSetGprsContextActivated.pfnNdisStatus) ||
             (FALSE ==NdisSetGprsContextActivated.fContextActivation)  )
			{
			// Force NULL-terminated string variables
			NdisSetGprsContextActivated.tszDNS1[ARRAY_LENGTH(NdisSetGprsContextActivated.tszDNS1)-1]= TCHAR ('\0');
			NdisSetGprsContextActivated.tszDNS2[ARRAY_LENGTH(NdisSetGprsContextActivated.tszDNS2)-1]= TCHAR ('\0');
			NdisSetGprsContextActivated.tszPassword[ARRAY_LENGTH(NdisSetGprsContextActivated.tszPassword)-1]= TCHAR ('\0');
			NdisSetGprsContextActivated.tszUserName[ARRAY_LENGTH(NdisSetGprsContextActivated.tszUserName)-1]= TCHAR ('\0');
		
			CPdpContext	*pCPdpContext = (CPdpContext*)pCrilHandle->CRilNdis.NdisPdpContextFromCid( NdisSetGprsContextActivated.dwContextID );
		
			RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisSetGPRSContextActivated  cid = %x, Activation = %s \r\n"), 
					 NdisSetGprsContextActivated.dwContextID, NdisSetGprsContextActivated.fContextActivation ? L"TRUE" : L"FALSE" ));


			AsyncCommandID = pCrilHandle->GetNextCmdID();
			pCrilHandle->CRilNdis.LockPdpContextList();
			if ( FALSE == pCrilHandle->CRilNdis.IsPdpContextListEmpty())
				{
				RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisSetGPRSContextActivated  Queueing Request Command ID = %x \r\n"), AsyncCommandID));
				Result = RIL_RESULT_OK;
				pCrilHandle->CRilNdis.AddPdpContextEntry(&NdisSetGprsContextActivated,AsyncCommandID,(PVOID)m_pCrilInstance);
				if ( !NdisSetGprsContextActivated.fContextActivation )
					{
					if (FALSE == WaitForCloseResponse())
						{
						pCrilHandle->CRilNdis.NdisRemoveCallbacks(NdisSetGprsContextActivated.dwContextID);
						((CRilInstanceHandle*)m_pCrilInstance)->Notify(Result, 
														AsyncCommandID, 
														&Result, 
														sizeof(Result));
						 pCrilHandle->CRilNdis.NdisRemoveCommand(AsyncCommandID);
						}
					}
				}
			else
				{
				if ( FALSE == NdisSetGprsContextActivated.fContextActivation )
					{
					if ( pCPdpContext) 
						{
						pCrilHandle->CRilNdis.NdisRemoveCallbacks(NdisSetGprsContextActivated.dwContextID);
						pCrilHandle->CRilNdis.NdisAllocateCommand ( OEM_NDIS_CLOSE_COMPLETE, NdisSetGprsContextActivated.dwContextID, (LPVOID)m_pCrilInstance, AsyncCommandID ); 
						Result = pCPdpContext->ActivateContext((PVOID) &NdisSetGprsContextActivated);
						if ( FAILED(Result))
							{
							RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisSetGPRSContextActivated  CLOSE handle = %x Call FAILED\r\n"), pCPdpContext));
							pCrilHandle->CRilNdis.NdisShutdownConnection ( NdisSetGprsContextActivated.dwContextID );
							}
						else
							{
							RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisSetGPRSContextActivated  CLOSE handle = %x \r\n"), pCPdpContext));
							pCrilHandle->CRilNdis.AddPdpContextEntry(&NdisSetGprsContextActivated,AsyncCommandID,(PVOID)m_pCrilInstance);
							if (FALSE == WaitForCloseResponse())
								{
								Result =  RIL_RESULT_OK;
								((CRilInstanceHandle*)m_pCrilInstance)->Notify(Result, 
														AsyncCommandID, 
														&Result, 
														sizeof(Result));
								pCrilHandle->CRilNdis.NdisRemoveCommand(AsyncCommandID);
								}
							}
						}
					else // No connection object - no connection
						{
						Result = RIL_E_INVALIDCONTEXTSTATE; 
						}
					}
				else 
					{
					if ( NULL == pCPdpContext) // This should normally be the case.
						{ 
						// Check to see if there are any available to initialise.
						if ( SUCCEEDED(pCrilHandle->CRilNdis.NdisGetFreeEntry( &Index)) )
							{
							pCPdpContext = new CPdpContext(NdisRxMsgQueueWriteHandle);
							if ( pCPdpContext )
								{
								pCrilHandle->CRilNdis.NdisAddNdisEntry(NdisSetGprsContextActivated.dwContextID,	Index, m_pCrilInstance, pCPdpContext, &NdisSetGprsContextActivated ); 
								pCrilHandle->CRilNdis.NdisAllocateCommand ( OEM_NDIS_OPEN_COMPLETE, NdisSetGprsContextActivated.dwContextID, (LPVOID)m_pCrilInstance, AsyncCommandID);
								Result = pCPdpContext->ActivateContext((PVOID) &NdisSetGprsContextActivated);
								if ( FAILED(Result))
									{
									pCrilHandle->CRilNdis.NdisRemoveNdisEntry(NdisSetGprsContextActivated.dwContextID);
									}
								else
									{
									pCrilHandle->CRilNdis.AddPdpContextEntry(&NdisSetGprsContextActivated,AsyncCommandID,(PVOID)m_pCrilInstance);
									}
								}
							}
						else
							{
							Result = RIL_E_MAXCONTEXTS;
							}
						}  
					else // connection object - connection
						{
						Result = RIL_E_INVALIDCONTEXTSTATE;
						}
					}
				}
				pCrilHandle->CRilNdis.UnlockPdpContextList();
			}
		}

	if ( FAILED(Result) )
		{
			pCrilHandle->CRilNdis.NdisRemoveCommand( AsyncCommandID );
			AsyncCommandID = Result;
		}

	return AsyncCommandID;
}

// **************************************************************************
// Function Name: NdisReceivePacketDone
// 
// Purpose: 	 Notify the NDIS radio that the Rx packet has been consumed.
//
// Arguments:	 IN Rx packet pointer
//
// Return Values:  E_FAIL if error detected.
//				   Command result for async response.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT CRilInstanceNDIS::NdisReceivePacketDone (RILNDISPACKET* lpNdisPacket)
{
	 return S_OK;
}

CAsyncResponse::CAsyncResponse()
{
	InitializeCriticalSection(&m_cs);
	InitializeListHead(&m_AsyncPendingList);
	InitializeListHead(&m_AsyncFreeList);
}


CAsyncResponse::~CAsyncResponse()
{
	PASYNCCOMMAND pEntry;
	BOOL ExitNow = FALSE;
	//
	// Find entry with matching ContextID in our list of pending requests
	//
	AsyncListLock();
	while ( FALSE == ExitNow )
		{
		for (pEntry = (PASYNCCOMMAND)(m_AsyncPendingList.Flink);
			 TRUE;
			 pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
			{
			if (pEntry == (PASYNCCOMMAND)(&m_AsyncPendingList))
				{
				ExitNow = TRUE;
				break;
				}

			RemoveEntryList(&pEntry->Node);
			InsertTailList(&m_AsyncFreeList, &pEntry->Node);
			break;
			}
		}
	ExitNow = FALSE;
	while ( FALSE == ExitNow )
		{
		for (pEntry = (PASYNCCOMMAND)(m_AsyncFreeList.Flink);
			 TRUE;
			 pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
			{
			if (pEntry == (PASYNCCOMMAND)(&m_AsyncFreeList))
				{
				ExitNow = TRUE;
				break;
				}
			RemoveEntryList(&pEntry->Node);
			LocalFree ( pEntry );
			break;
			}
		}
	AsyncListUnlock();
}


void 
CAsyncResponse::AddCommand ( OEMNDISAPIREFERENCE *lpOemNdisApiReference )
{

	PASYNCCOMMAND pEntry = NULL;

	if ( lpOemNdisApiReference )
		{
		AsyncListLock();
		if (!IsListEmpty(&m_AsyncFreeList))
			{
			pEntry = (PASYNCCOMMAND)RemoveHeadList(&m_AsyncFreeList);
			}

		if (pEntry == NULL)
			{
			pEntry = (PASYNCCOMMAND)LocalAlloc(LPTR, sizeof(*pEntry));
			}

		if ( pEntry )
			{
			memcpy(&pEntry->OemNdisApiReference, lpOemNdisApiReference, sizeof(OEMNDISAPIREFERENCE));
			}
		InsertTailList(&m_AsyncPendingList, &pEntry->Node);
		AsyncListUnlock();
		}
}

void 
CAsyncResponse::RemoveCommand( HRESULT CommandID )
{
	if ( CommandID )
		{
		PASYNCCOMMAND pEntry;
  
		AsyncListLock();
		for (pEntry = (PASYNCCOMMAND)(m_AsyncPendingList.Flink);
			 TRUE;
			 pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
			{
			if (pEntry == (PASYNCCOMMAND)(&m_AsyncPendingList))
				{
				RETAILMSG(MSG_ON, (TEXT("RILNDIS: RemoveCommand  CommandID = %x NOT FOUND\r\n"), CommandID));
				break;
				}

			if ( pEntry->OemNdisApiReference.CommandID == CommandID )
				{
				RemoveEntryList(&pEntry->Node);
				InsertTailList(&m_AsyncFreeList, &pEntry->Node);
				break;
				}
			}
		AsyncListUnlock();
		}
}


void 
CAsyncResponse::ProcessCommand ( const OEMNDISAPIREFERENCE *lpOemNdisApiReference )
{
	
	if ( lpOemNdisApiReference )
		{
		CRilInstanceHandle* pCRilInstance=NULL;
		PASYNCCOMMAND pEntry;
	  

		AsyncListLock();  
		for (pEntry = (PASYNCCOMMAND)(m_AsyncPendingList.Flink);
			 TRUE;
			 pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
			{

			if (pEntry == (PASYNCCOMMAND)(&m_AsyncPendingList))
			{
			RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessCommand Notify FAILED \r\n")));
			break;
			}

			// Result for other event. Success is passed via the status member of the input structure.
			if ( lpOemNdisApiReference->EventType )
				{
				if ( (lpOemNdisApiReference->EventType == pEntry->OemNdisApiReference.EventType) &&
					(lpOemNdisApiReference->ContextID == pEntry->OemNdisApiReference.ContextID))
					{
					pCRilInstance = (CRilInstanceHandle*)pEntry->OemNdisApiReference.pCRilInstance;
					if ( pCRilInstance )
						{
						DWORD Result = lpOemNdisApiReference->Status ? RIL_RESULT_ERROR : RIL_RESULT_OK;
						pCRilInstance->Notify(	Result, 
									pEntry->OemNdisApiReference.CommandID, 
									&lpOemNdisApiReference->RilNdisGprsContextResponse, 
									sizeof(lpOemNdisApiReference->RilNdisGprsContextResponse));
						RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessCommand Notify Command Status = %x Comamand ID = %0x \r\n"), 
									Result, 
									pEntry->OemNdisApiReference.CommandID));
						}
					RemoveEntryList(&pEntry->Node);
					InsertTailList(&m_AsyncFreeList, &pEntry->Node);
					break;
					}
				}
			}
		AsyncListUnlock();
		}
}

void 
CAsyncResponse::RemoveAllCommands( DWORD ContextID )
{
	PASYNCCOMMAND pEntry;
	BOOL ExitNow = FALSE;
	//
	// Find entry with matching ContextID in our list of pending requests
	//
	AsyncListLock();
	while ( FALSE == ExitNow )
		{
		for (pEntry = (PASYNCCOMMAND)(m_AsyncPendingList.Flink);
			 TRUE;
			 pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
			{
			if (pEntry == (PASYNCCOMMAND)(&m_AsyncPendingList))
				{
				ExitNow = TRUE;
				break;
				}
			if ( ContextID == pEntry->OemNdisApiReference.ContextID )
				{
				RemoveEntryList(&pEntry->Node);
				InsertTailList(&m_AsyncFreeList, &pEntry->Node);
				break;
				}
			}
		}
	AsyncListUnlock();
}

inline void
CAsyncResponse::AsyncListLock()
{
	EnterCriticalSection(&m_cs);
}

inline void
CAsyncResponse::AsyncListUnlock()
{
	LeaveCriticalSection(&m_cs);
}
DWORD WINAPI ProcessObjectsThreadProc(LPVOID pParameter);


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CPdpContext::CPdpContext(PVOID pEventNotification )
{
	InitializeCriticalSection(&m_Lock);
	m_ProcessObjextsThreadID = 0;
	m_CrilCancelEvent = NULL;
	m_ContextID = 0;
	m_CrilAsyncCommandDone = NULL;
	m_RilResultCode = 0;
	m_ActivationState = ACTIVATION_STATE_DISCONNECTED;
	memset(&m_OemNdisIpConfig, 0, sizeof(m_OemNdisIpConfig));

	if ( pEventNotification )
		{
		m_AppMsgQueue = pEventNotification;
		
		m_ProcessObjectsThread = CreateThread(NULL, 0, ProcessObjectsThreadProc, (LPVOID)this, 0, &m_ProcessObjextsThreadID);
		}

	RETAILMSG(MSG_ON, (TEXT("RILNDIS CPdpContext: CPdpContext Create Connection Object %x \r\n"), this));
}

CPdpContext::~CPdpContext()
{
	DWORD ExitCode;
	DeleteCriticalSection(&m_Lock);

	if ( m_ProcessObjectsThread  )
		{
		if ( NULL == m_CrilCancelEvent )
			{
			if ( GetExitCodeThread(m_ProcessObjectsThread, &ExitCode ))
				{
				if ( STILL_ACTIVE == ExitCode )
					{
					Sleep(100);
					}
				}
			}
		
		if ( m_CrilCancelEvent )
			{
			SetEvent(m_CrilCancelEvent);
			WaitForSingleObject(m_ProcessObjectsThread, INFINITE );
			CloseHandle(m_ProcessObjectsThread);
			}
		}
	RETAILMSG(MSG_ON, (TEXT("RILNDIS CPdpContext: CPdpContext Delete Connection Object %x \r\n"), this));
}

inline void
CPdpContext::Lock()
{
	EnterCriticalSection(&m_Lock);
}

inline void
CPdpContext::Unlock()
{
	LeaveCriticalSection(&m_Lock);
}

inline BOOL
CPdpContext::IsLocked()
{
	return m_Lock.OwnerThread == (HANDLE)GetCurrentThreadId();
}


// **************************************************************************
// Function Name:  ProcessObjectsThreadProc
// 
// Purpose: 	   Process NDIS events from radio.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
DWORD WINAPI ProcessObjectsThreadProc(LPVOID pParameter)
{
	CPdpContext* pCPdpContext = (CPdpContext*)pParameter;
	DWORD Result;

	Result = pCPdpContext->ProcessObjectsThread();

	return Result;
}



HRESULT 							
CPdpContext::GetGPRSContextActivatedList(void)
{
	HRESULT Result;
	Result = S_OK;//AsyncRequestIssue((RIL_FUNCTION)RIL_GetGPRSContextActivatedList, NULL, NULL, CRilRequestTypeGetGPRSContextActivatedList, Arg);
	return Result;
}

HRESULT 							
CPdpContext::GetGPRSAddress(
	IN DWORD ContextID)

{
	FUNCTION_TRACE(RILDrv_GetGPRSAddress);

	HRESULT Result = S_OK;

	// Build command
	char Cmd[MAX_PATH];
	(void)_snprintfz(Cmd, MAX_PATH, "AT+CGPADDR=%u\r", ContextID);

	if (!QueueCmdIgnoreRspWithData(APIID_GETGPRSADDRESS,	// appiid
								   Cmd,		  // szCmd
								   CMDOPT_FORCEPARSE,	// dwOptions
								   20000,  // dwTimeout
								   NULL,  // Parse fn
								   NULL,	 // Ptr to notification data
								   0,		// dwRetries
								   0,		// dwRetriesOnError
								   0,	  // dwRetryOnErrorDelay
								   CRilParseGetGPRSAddress,//parse fn with data
								   this  ))   // Data ))	// dwRetryOnErrorDelay
		{
		Result = E_FAIL;
		}

	return Result;
}


//
//
//
HRESULT 
CPdpContext::DevSpecific(
	IN BYTE* pParams,	 // @parm parameters for the operation to be performed
	IN DWORD Size,	   // @parm size of the data pointed to by <p lpParams> in bytes
	IN BOOL IgnoreResponse
)
{
	PFN_CMD_PARSE_DATA pFnCmdParseData = NULL;
	HRESULT Result = S_OK;
	char Cmd[MAX_PATH];

	if (!pParams) 
		{
		Result = E_FAIL;
		goto Error;
		}

	DEBUGCHK(NULL != pParams);
	DEBUGCHK(Size > 0);

	if ( Size != sizeof(RILATCOMMAND))
		{
		Result = E_NOTIMPL;
		goto Error;
		}

	if ( Size == sizeof(RILATCOMMAND) )
		{
		LPRILATCOMMAND pRilATCommand = (LPRILATCOMMAND)pParams;
		if ( pRilATCommand && pRilATCommand->Size == sizeof(*pRilATCommand))
			{
			switch ( pRilATCommand->Operation)
				{
				case CRilATCommandTypeCGPCO:
					{
					LPRILATCGPCO pRilCGPCO = (LPRILATCGPCO)pRilATCommand->pData;
					if (!pRilCGPCO)
						{
						Result = E_FAIL;
						goto Error;
						}
				
					if ( pRilCGPCO->Size == sizeof(*pRilCGPCO) )
						{
						// Build command
						char ATCommand[] = "AT%CGPCO=";
						if ( 0 == pRilCGPCO->Mode)// Set
							{
							pFnCmdParseData = IgnoreResponse ? NULL : CRilParseSetCGPCO;
							(void)_snprintfz(Cmd, MAX_PATH, "%s%u,1,\"%s\",%u\r",ATCommand,pRilCGPCO->Mode,pRilCGPCO->ProtocolOptions, pRilCGPCO->ContextID);
							if (!QueueCmdIgnoreRspWithData(APIID_GETGPRSADDRESS,	// appiid
												   Cmd,		  // szCmd
												   CMDOPT_FORCEPARSE,	// dwOptions
												   20000,  // dwTimeout
												   NULL,  // Parse fn
												   NULL,	 // Ptr to notification data
												   0,		// dwRetries
												   0,		// dwRetriesOnError
												   0,	  // dwRetryOnErrorDelay
												   pFnCmdParseData,//parse fn with data
												   this  ))   // Data ))	// dwRetryOnErrorDelay
								{
								Result = E_FAIL;
								goto Error;
								}
							}
						else // Query
							{
							pFnCmdParseData = IgnoreResponse ? NULL : CRilParseGetCGPCO;
							(void)_snprintfz(Cmd, MAX_PATH, "%s%u,1,,%u\r",ATCommand,pRilCGPCO->Mode,pRilCGPCO->ContextID);
							if (!QueueCmdIgnoreRspWithData(APIID_GETGPRSADDRESS,	// appiid
												   Cmd,		  // szCmd
												   CMDOPT_FORCEPARSE,	// dwOptions
												   20000,  // dwTimeout
												   NULL,  // Parse fn
												   NULL,	 // Ptr to notification data
												   0,		// dwRetries
												   0,		// dwRetriesOnError
												   0,	  // dwRetryOnErrorDelay
												   pFnCmdParseData,//parse fn with data
												   this  ))   // Data ))	// dwRetryOnErrorDelay
								{
								Result = E_FAIL;
								goto Error;
								}
							}
							
						}  
					else
						{
						Result = E_FAIL;
						}  
					}
				break;
		
				case CRilATCommandTypeDATA:
				{
					LPRILATDATA pRilData = (LPRILATDATA)pRilATCommand->pData;
					if (!pRilData)
						{
						Result = E_FAIL;
						goto Error;
						}
					if ( pRilData->Size == sizeof(*pRilData) )
						{
						pFnCmdParseData = IgnoreResponse ? NULL : CRilParseDATA;
						// Build command
						char ATCommand[] = "AT%DATA=";
						(void)_snprintfz(Cmd, MAX_PATH, "%s%u,\"%s\",%u,%u,\"%s\",\"%s\",%u,%u,%u\r",
										 ATCommand,
										 pRilData->Mode,
										 pRilData->DestDeviceName,
										 pRilData->DestDeviceNum,
										 pRilData->DestSubNum,
										 pRilData->Capability,
										 pRilData->SrcDevName,
										 pRilData->SrcDevNum,
										 pRilData->SrcSubNum,
										 pRilData->ContextID);
		
					   if (!QueueCmdIgnoreRspWithData( APIID_GETGPRSADDRESS,	// appiid
											   Cmd,		  // szCmd
											   CMDOPT_FORCEPARSE,	// dwOptions
											   20000,  // dwTimeout
											   NULL,  // Parse fn
											   NULL,  // Ptr to notification data
											   0,		// dwRetries
											   0,		// dwRetriesOnError
											   0,	  // dwRetryOnErrorDelay
											   pFnCmdParseData,//parse fn with data
											   this  ))   // Data ))	// dwRetryOnErrorDelay

							{
							Result = E_FAIL;
							goto Error;
							}
						}
					else
						{
						Result = E_FAIL;
						}
					}
				break;
		
				case CRilATCommandTypeCGDATA:
				{
					LPRILATCGDATA pRilCGData = (LPRILATCGDATA)pRilATCommand->pData;
					if (!pRilCGData)
						{
						Result = E_FAIL;
						goto Error;
						}
					if ( pRilCGData->Size == sizeof(*pRilCGData) )
						{
						pFnCmdParseData = IgnoreResponse ? NULL : CRilParseCGDATA;
						// Build command
						char ATCommand[] = "AT+CGDATA=";
						(void)_snprintfz(Cmd, MAX_PATH, "%s\"%s\",%u\r",ATCommand,pRilCGData->L2P,pRilCGData->ContextID);
						if (!QueueCmdIgnoreRspWithData( APIID_GETGPRSADDRESS,	 // appiid
											   Cmd,		  // szCmd
											   CMDOPT_FORCEPARSE,	// dwOptions
											   20000,  // dwTimeout
											   NULL,  // Parse fn
											   NULL,  // Ptr to notification data
											   0,		// dwRetries
											   0,		// dwRetriesOnError
											   0,	  // dwRetryOnErrorDelay
											   pFnCmdParseData,//parse fn with data
											   this  ))   // Data ))	// dwRetryOnErrorDelay
							{
							Result = E_FAIL;
							goto Error;
							}
						}
					else
						{
						Result = E_FAIL;
						}
				}
				break;

				case CRilATCommandTypeCGACT:
				{
					LPRILATCGACT pRilCGAct = (LPRILATCGACT)pRilATCommand->pData;
					if (!pRilCGAct)
						{
						Result = E_FAIL;
						goto Error;
						}
					if ( pRilCGAct->Size == sizeof(*pRilCGAct) )
						{
						DWORD dwOptions = CMDOPT_FORCEPARSE;
						DWORD dwTimeout = pRilCGAct->ContextActivation ? 180000 : 90000;
						if(FALSE == IncomingCallInProgress())
							{
							dwOptions |= (pRilCGAct->ContextActivation ? 0 : CMDOPT_DEACT);
							}
						pFnCmdParseData = IgnoreResponse ? NULL : CRilParseCGACT;
						// Build command
						(void)_snprintfz(Cmd, MAX_PATH, "AT+CGACT=%u,%u\r", pRilCGAct->ContextActivation ? 1 : 0, pRilCGAct->ContextID);
						if (!QueueCmdIgnoreRspWithData( APIID_SETGPRSCONTEXTACTIVATED,	 // appiid
											   Cmd,		  // szCmd
											   dwOptions,	// dwOptions
											   dwTimeout,	// dwTimeout
											   NULL,  // Parse fn
											   NULL,  // Ptr to notification data
											   0,		// dwRetries
											   0,		// dwRetriesOnError
											   0,		// dwRetryOnErrorDelay
											   pFnCmdParseData,//parse fn with data
											   this  ))   // Data 
							{
							Result = E_FAIL;
							goto Error;
							}
						}
					else
						{
						Result = E_FAIL;
						}
				}
				break;

				default:
				{
					Result = E_NOTIMPL;
					goto Error;
				}
				}
			}
		}

Error:
	return Result;
}

void
CPdpContext::SetOpenComplete ( DWORD RilResultCode	)
{
	OEMNDISEVENT  OemNdisEvent;
	BOOL Result;

	Lock();
	memset(&OemNdisEvent, 0, sizeof(OEMNDISEVENT));
	CeSafeCopyMemory (&OemNdisEvent.IpConfig, &m_OemNdisIpConfig, sizeof(m_OemNdisIpConfig));
	OemNdisEvent.Type = OEM_NDIS_OPEN_COMPLETE;
	OemNdisEvent.Status = RilResultCode; 
	OemNdisEvent.ContextID = m_ContextID;
	Result = WriteMsgQueue(m_AppMsgQueue, &OemNdisEvent, sizeof(OemNdisEvent),INFINITE, 0 );
	ASSERT ( Result );
	Unlock();
}

void
CPdpContext::SetNetworkDisconnect ( BOOL GsmRegistered, BOOL GprsAttached )
{
	OEMNDISEVENT  OemNdisEvent;
	BOOL Result;

	Lock();
	memset(&OemNdisEvent, 0, sizeof(OEMNDISEVENT));
	OemNdisEvent.ContextID = m_ContextID;
//		pOemNdisEvent->Type = OEM_NDIS_REGISTRATION_STATUS;
//		pOemNdisEvent->event.regStatus.fGsmRegistered = GsmRegistered; 
//		pOemNdisEvent->event.regStatus.fGprsAttached = GprsAttached;
	Result = WriteMsgQueue(m_AppMsgQueue, &OemNdisEvent, sizeof(OemNdisEvent),INFINITE, 0 );
	ASSERT ( Result );
	Unlock();
}

void
CPdpContext::SetCloseComplete ( void )
{
	OEMNDISEVENT  OemNdisEvent;
	BOOL Result;

	Lock();
	memset(&OemNdisEvent, 0, sizeof(OEMNDISEVENT));
	OemNdisEvent.Type = OEM_NDIS_CLOSE_COMPLETE;
	OemNdisEvent.ContextID = m_ContextID;
	Result = WriteMsgQueue(m_AppMsgQueue, &OemNdisEvent, sizeof(OemNdisEvent),INFINITE, 0 );
	ASSERT ( Result );
	Unlock();
}

void								
CPdpContext::InternalIPV4GPRSAddress ( DWORD GPRSIPAddress )
{
	CeSafeCopyMemory (&m_OemNdisIpConfig.ipAddress.address, &GPRSIPAddress, sizeof(GPRSIPAddress));
	m_OemNdisIpConfig.ipAddress.length = 4; // bytes
}

void
CPdpContext::InternalIPV4DNS( DWORD DNS1, DWORD DNS2, DWORD Gateway )
{
	CeSafeCopyMemory (&m_OemNdisIpConfig.primaryDnsAddress.address, &DNS1, sizeof(DNS1));
	m_OemNdisIpConfig.primaryDnsAddress.length = 4; // bytes
	CeSafeCopyMemory (&m_OemNdisIpConfig.secondaryDnsAddress.address, &DNS2, sizeof(DNS2));
	m_OemNdisIpConfig.secondaryDnsAddress.length = 4; // bytes
	CeSafeCopyMemory (&m_OemNdisIpConfig.gateway.address, &Gateway, sizeof(Gateway));
	m_OemNdisIpConfig.gateway.length = 4; // bytes

}

DWORD
CPdpContext::ProcessObjectsThread ( void )
{
	HANDLE			MultipleObjects[2] = {NULL,NULL};
	RILATCGACT		RilCgAct;
	RILATCOMMAND	RilATCommand;
	RILATCGDATA 	RilCgData;
	RILATCGPCO		RilCgpco;
#if defined(ENFORA_DRIVER)
	RILATDATA		RilATData;
#endif
	HRESULT 		Result;
	int 			NumEvents=0;
	
	MultipleObjects[0] = m_CrilAsyncCommandDone = CreateEvent(NULL,FALSE,FALSE,NULL);
	NumEvents++;
	MultipleObjects[1] = m_CrilCancelEvent = CreateEvent(NULL, FALSE, FALSE,NULL);
	NumEvents++;
	if ( m_AppMsgQueue && MultipleObjects[0] && MultipleObjects[1] )
		{
		DWORD WaitStatus;	   
		RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread Running \r\n"));


		while ( 1 )
			{
			WaitStatus = WaitForMultipleObjects(NumEvents, MultipleObjects, FALSE, INFINITE);
			RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread Process Object Index = %x \r\n",WaitStatus - WAIT_OBJECT_0));


			if ( 0 == (WaitStatus - WAIT_OBJECT_0))// Command Response
				{
				switch ( m_ActivationState)
					{
					case ACTIVATION_STATE_CGPCO_SET:
						RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread ACTIVATION_STATE_CGPCO_SET dwRilResultCode = %x\r\n", m_RilResultCode ));
						if ( RIL_RESULT_OK == m_RilResultCode )
							{
							m_ActivationState = ACTIVATION_STATE_ACT;

							RilCgAct.Size = sizeof(RilCgAct);
							RilCgAct.ContextID = m_ContextID;
							RilCgAct.ContextActivation = TRUE;
							RilATCommand.Size = sizeof(RilATCommand);
							RilATCommand.Operation = CRilATCommandTypeCGACT;
							RilATCommand.pData = &RilCgAct;

							Result = DevSpecific((BYTE*)&RilATCommand, sizeof(RilATCommand),FALSE);
							if ( FAILED( Result ))
								{
								m_RilResultCode = Result;
								}
							}

						if ( RIL_RESULT_OK != m_RilResultCode )
							{
							m_ActivationState = ACTIVATION_STATE_OPEN_FAILED;
							SetEvent( m_CrilAsyncCommandDone );
							}
						break;

#if defined(ENFORA_DRIVER)
					case ACTIVATION_STATE_ACT:
						RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread ACTIVATION_STATE_ACT dwRilResultCode = %x\r\n", m_RilResultCode));
						if ( RIL_RESULT_OK == m_RilResultCode )
							{						    
							m_ActivationState = ACTIVATION_STATE_DATA;

							RilATData.Size = sizeof(RilATData);
							RilATData.Mode = 1;							
							switch(m_ContextID)
								{
								case 1:
									RilATData.DestDeviceNum = PKT1_DLCI;
									break;
								case 2:
									RilATData.DestDeviceNum = PKT2_DLCI;
									break;
								case 3:
									RilATData.DestDeviceNum = PKT3_DLCI;
									break;
								default:
									m_RilResultCode = E_FAIL;
									break;									
								}
							if ( RIL_RESULT_OK != m_RilResultCode )
								{
								m_ActivationState = ACTIVATION_STATE_OPEN_FAILED;
								SetEvent( m_CrilAsyncCommandDone );
								break;
								}
							RilATData.DestSubNum = 0;
							strcpy(RilATData.DestDeviceName, "PKTIO"); 
							strcpy(RilATData.Capability, "PKT");
							strcpy(RilATData.SrcDevName, "UART");
							RilATData.SrcDevNum = 0;
							RilATData.SrcSubNum = ATCMD_DLCI;
							RilATData.ContextID = m_ContextID;

							RilATCommand.Size = sizeof(RilATCommand);
							RilATCommand.Operation = CRilATCommandTypeDATA;
							RilATCommand.pData = &RilATData;
							Result = DevSpecific((BYTE*)&RilATCommand, sizeof(RilATCommand),FALSE);
							if ( FAILED( Result ))
								{
								m_RilResultCode = Result;
								}
							}

						if ( RIL_RESULT_OK != m_RilResultCode )
							{
							m_ActivationState = ACTIVATION_STATE_OPEN_FAILED;
							SetEvent( m_CrilAsyncCommandDone );
							}
						break;

					case ACTIVATION_STATE_DATA:
						RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread ACTIVATION_STATE_DATA dwRilResultCode = %x\r\n", m_RilResultCode));
						if ( RIL_RESULT_OK == m_RilResultCode )
							{
							m_ActivationState = ACTIVATION_STATE_CGPADDR;
							Result = GetGPRSAddress(m_ContextID);
							if ( FAILED( Result ))
								{
								m_RilResultCode = Result;
								}
							}

						if ( RIL_RESULT_OK != m_RilResultCode )
							{
							m_ActivationState = ACTIVATION_STATE_OPEN_FAILED;
							SetEvent( m_CrilAsyncCommandDone );
							}
						break;
#else
					case ACTIVATION_STATE_ACT:
						RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread ACTIVATION_STATE_ACT dwRilResultCode = %x\r\n", m_RilResultCode));
						if ( RIL_RESULT_OK == m_RilResultCode )
							{
							m_ActivationState = ACTIVATION_STATE_CGPADDR;
							Result = GetGPRSAddress(m_ContextID);
							if ( FAILED( Result ))
								{
								m_RilResultCode = Result;
								}
							}

						if ( RIL_RESULT_OK != m_RilResultCode )
							{
							m_ActivationState = ACTIVATION_STATE_OPEN_FAILED;
							SetEvent( m_CrilAsyncCommandDone );
							}
						break;
#endif

					case ACTIVATION_STATE_CGPADDR:
						RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread ACTIVATION_STATE_CGPADDR dwRilResultCode = %x\r\n", m_RilResultCode));
						if ( RIL_RESULT_OK == m_RilResultCode )
							{						
							RilCgpco.Size = sizeof(RilCgpco);
							RilCgpco.ContextID = m_ContextID;
							RilCgpco.Mode = 1;
							
							RilATCommand.Size = sizeof(RilATCommand);
							RilATCommand.Operation = CRilATCommandTypeCGPCO;
							RilATCommand.pData = &RilCgpco;
							m_ActivationState = ACTIVATION_STATE_CGPCO_QUERY;
							Result = DevSpecific((BYTE*)&RilATCommand, sizeof(RilATCommand),FALSE);
							if ( FAILED( Result ))
								{
								m_RilResultCode = Result;
								}
							}

						if ( RIL_RESULT_OK != m_RilResultCode )
							{
							m_ActivationState = ACTIVATION_STATE_OPEN_FAILED;
							SetEvent( m_CrilAsyncCommandDone );
							}
						break;

					case ACTIVATION_STATE_CGPCO_QUERY:
						RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread ACTIVATION_STATE_CGPCO_QUERY dwRilResultCode = %x\r\n", m_RilResultCode));
						if ( RIL_RESULT_OK == m_RilResultCode )
							{
							m_ActivationState = ACTIVATION_STATE_CGDATA;
							RilCgData.Size = sizeof(RilCgData);
							RilCgData.ContextID = m_ContextID;
							strcpy (RilCgData.L2P, "M-PKT" );
							RilATCommand.Size = sizeof(RilATCommand);
							RilATCommand.Operation = CRilATCommandTypeCGDATA;
							RilATCommand.pData = &RilCgData;
							Result = DevSpecific((BYTE*)&RilATCommand, sizeof(RilATCommand),FALSE);
							if ( FAILED( Result ))
								{
								m_RilResultCode = Result;
								}
							}

						if ( RIL_RESULT_OK != m_RilResultCode )
							{
							m_ActivationState = ACTIVATION_STATE_OPEN_FAILED;
							SetEvent( m_CrilAsyncCommandDone );
							}

						break;
					
					case ACTIVATION_STATE_CGDATA:
						RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread ACTIVATION_STATE_CGDATA dwRilResultCode = %x\r\n", m_RilResultCode));
						if ( RIL_RESULT_OK == m_RilResultCode )
							{
							m_ActivationState = ACTIVATION_STATE_OPEN_SUCCESS;
							}
						else
							{
							m_ActivationState = ACTIVATION_STATE_OPEN_FAILED;
							   
							}
						SetEvent( m_CrilAsyncCommandDone );
						break;	  

					case ACTIVATION_STATE_OPEN_FAILED:
						RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread ACTIVATION_STATE_OPEN_FAILED dwRilResultCode = %x\r\n", m_RilResultCode));
						SetOpenComplete( m_RilResultCode );
						m_ActivationState = ACTIVATION_STATE_DISCONNECTED;
						break;

					case ACTIVATION_STATE_OPEN_SUCCESS:
						RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread ACTIVATION_STATE_OPEN_SUCCESS dwRilResultCode = %x\r\n", m_RilResultCode));
						SetOpenComplete( 0 );
						m_ActivationState = ACTIVATION_STATE_CONNECTED;
						break;

					case ACTIVATION_STATE_REMOTE_DISCONNECT:
						RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread ACTIVATION_STATE_REMOTE_DISCONNECT dwRilResultCode = %x\r\n", m_RilResultCode));
						m_ActivationState = ACTIVATION_STATE_DISCONNECTED;
						//SetNetworkDisconnect ( TRUE, FALSE );
						break;

					case ACTIVATION_STATE_DEACTIVATE:
						RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread ACTIVATION_STATE_DEACTIVATE dwRilResultCode = %x\r\n", m_RilResultCode));
						m_ActivationState = ACTIVATION_STATE_DISCONNECTED;
						SetCloseComplete ( );
						break;


					default:
						RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread m_ActivationState = %x\r\n", m_ActivationState ));
						ASSERT(0);
						break;
	
					}
				}
			
			else if ( 1 == (WaitStatus - WAIT_OBJECT_0)) // Cancel
				{
				break;
				}
			else // Timeout
				{
				ASSERT(0);
				}
			}
		}

	for(int i=0; i<NumEvents;i++)
		{
		if ( MultipleObjects[i] )
			{
			CloseHandle( MultipleObjects[i] );
			m_CrilCancelEvent = 0;
			m_CrilAsyncCommandDone = 0;
			}
		}
	RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: ProcessObjectsThread Exiting \r\n"));

	return 0;

}

void
CPdpContext::CloseContext( DWORD ContextID )
{
	HRESULT 	 Result = E_FAIL;
	RILATCGACT	 RilCgAct;
	RILATCOMMAND RilATCommand;

	if ( m_CrilCancelEvent )
		{
		m_ActivationState = ACTIVATION_STATE_DEACTIVATE;
		RilCgAct.Size = sizeof(RilCgAct);
		RilCgAct.ContextID = m_ContextID;
		RilCgAct.ContextActivation = FALSE;
		RilATCommand.Size = sizeof(RilATCommand);
		RilATCommand.Operation = CRilATCommandTypeCGACT;
		RilATCommand.pData = &RilCgAct;
		Result = DevSpecific((BYTE*)&RilATCommand, sizeof(RilATCommand),TRUE);
		}
}

HRESULT
CPdpContext::ActivateContext(PVOID pActivationParameters)
{
	HRESULT 	 Result = E_FAIL;
	RILATCGACT	 RilCgAct;
	RILATCOMMAND RilATCommand;
	RILATCGPCO	 RilCgpco;
	RILNDISGPRSCONTEXT* pRilGprsContext = (RILNDISGPRSCONTEXT*)pActivationParameters;

	if ( NULL == m_CrilCancelEvent )
		{
		if ( m_ProcessObjectsThread )
			{
			for(int i=0; i<10; i++)
				{
				if ( NULL == m_CrilCancelEvent )
					{
					DWORD ExitCode=0;
					if ( GetExitCodeThread(m_ProcessObjectsThread, &ExitCode ))
						{
						if ( STILL_ACTIVE == ExitCode )
							{
							Sleep(100);
							continue;
							}
						}
					}
				else
					{
					break;
					}
				}
			}
		}

	if ( m_CrilCancelEvent )
		{
		if ( pRilGprsContext )
			{
			if ( pRilGprsContext->fContextActivation )
				{
				RilCgpco.Size = sizeof(RilCgpco);
				RilCgpco.ContextID = pRilGprsContext->dwContextID;
				m_ContextID = pRilGprsContext->dwContextID;
				RilCgpco.Mode = 0;
				if ( 0 == (pRilGprsContext->dwParams & RIL_PARAM_RILNDISGPRSCONTEXT_USERNAME))
					{
					(void)_snprintfz(RilCgpco.ProtocolOptions, MAX_ATCGPCO_OPTIONS_LEN - 1 , "%s,,", "PAP");
					}
				else
					{
					(void)_snprintfz(RilCgpco.ProtocolOptions, MAX_ATCGPCO_OPTIONS_LEN - 1, "%s,%s,%s", "PAP",AnsiString(pRilGprsContext->tszUserName), AnsiString(pRilGprsContext->tszPassword));
					}
				
				RilATCommand.Size = sizeof(RilATCommand);
				RilATCommand.Operation = CRilATCommandTypeCGPCO;
				RilATCommand.pData = &RilCgpco;
				m_ActivationState = ACTIVATION_STATE_CGPCO_SET;
				Result = DevSpecific((BYTE*)&RilATCommand, sizeof(RilATCommand),FALSE);
				}
			else
				{
				m_ActivationState = ACTIVATION_STATE_DEACTIVATE;
				RilCgAct.Size = sizeof(RilCgAct);
				RilCgAct.ContextID = m_ContextID;
				RilCgAct.ContextActivation = FALSE;
				RilATCommand.Size = sizeof(RilATCommand);
				RilATCommand.Operation = CRilATCommandTypeCGACT;
				RilATCommand.pData = &RilCgAct;
				Result = DevSpecific((BYTE*)&RilATCommand, sizeof(RilATCommand),FALSE);
				}
			}
		}

	if ( FAILED(Result) )
		{
		m_RilResultCode = RIL_RESULT_ERROR;
		SetEvent( m_CrilAsyncCommandDone );
		}
	return Result;
}


DWORD ExtractIPV4Address ( int StringLength, LPCSTR szAddr)
{
	DWORD IpAddress = 0;
	LPSTR szTempString=NULL;;
	int stringCountIndex,stringCopyIndex;

	szTempString = (LPSTR)AllocBlob(StringLength * sizeof(CHAR));
	RETAILMSG(MSG_ON,(TEXT("RILNDIS ExtractIPV4Address szAddr = %hs, StringLength = %d \r\n"),szAddr, StringLength));

	if (  szTempString )
		{
		DWORD	TempIpAddress;
		DWORD	DottedPlace = 0;
		for( stringCountIndex=0,  stringCopyIndex=0; stringCountIndex<StringLength; stringCountIndex++)
			{

			szTempString[stringCopyIndex] = szAddr[stringCountIndex];

			if ( (szAddr[stringCountIndex] == '.') || (stringCountIndex == (StringLength - 1)) )
				{
				szTempString[stringCopyIndex] = 0;	 
				TempIpAddress = atoi( szTempString );
				stringCopyIndex = 0;
				TempIpAddress <<= (8 * DottedPlace);
				DottedPlace++;
				IpAddress |= TempIpAddress;
				} 
			else
				{
				stringCopyIndex++;
				}		   
			}
			FreeBlob(szTempString);
		}
	return IpAddress;
}
//
//
//
BOOL ParseOK(const BOOL ZeroOrOne, LPCSTR pRsp, DWORD *pNewLength)
{
	LPSTR pResponse;
	CeSafeCopyMemory(&pResponse, &pRsp, sizeof(pResponse));
	LPCSTR pCode = (ZeroOrOne ? "0\r" : "1\r");
	BOOL Return;

	while (1)
		{
		// First search in the beginning of the response
		Return = MatchStringBeginning(pResponse, pCode, pResponse);
		if (Return && '\n' != *pResponse)
			{
			// We found "<code><CR>" not followed by <LF> in the beginning of the response
			break;
			}
		else
			{
			// Now search elsewhere in the response
			Return = MatchStringAnywhere(pResponse, pCode, pResponse);
			if (!Return)
				{
				// We didn't find "<code><CR>"
				break;
				}
			else if ('\n' != *pResponse &&
					 ('\n' == *(pResponse - 3) || '\r' == *(pResponse - 3)))
				{
				// We found "<code><CR>" not followed by <LF> and preceded by <LF> or <CR>
				break;
				}
			}
		}

	if (Return)
		{
		*pNewLength = pResponse - pRsp;
		}

	return Return;
}

//
// Comparison routine used for binary search below
//
static int _cdecl CGACTBSCompareErrors(const void* pElem1, const void* pElem2)
{
    DWORD dwCode1 = ((ERRORMAP*)pElem1)->dwCode;
    DWORD dwCode2 = ((ERRORMAP*)pElem2)->dwCode;
    int iRet;

    if (dwCode1 < dwCode2)
    {
        iRet = -1;
    }
    else if (dwCode1 == dwCode2)
    {
        iRet = 0;
    }
    else
    {
        iRet = 1;
    }
    return iRet;
}

//
//
//
void ParseJunk(UINT nCRLFs, LPCSTR& rszPointer)
{
    DEBUGCHK(0 < nCRLFs);
    LPCSTR szPointerTmp = rszPointer;

    UINT i;

    // Try to match all the CRLFs we should have
    for (i = 0; i < nCRLFs; i++)
    {
        if (!MatchStringAnywhere(szPointerTmp, "\r\n", szPointerTmp))
        {
            break;
        }
    }

    // Back up over the last <CR><LF> iff we found one
    if (szPointerTmp > (rszPointer+2))
    {
        rszPointer = szPointerTmp-2;
    }
}


HRESULT ParseExtError(LPCSTR& rszPointer, const ERRORMAP* const rgemMap, const UINT nMapEntries, UINT &nCode, BOOL fSigned)
{
    LPCSTR szDummy;
    BOOL fRet = FALSE;
    ERRORMAP emKey;
    ERRORMAP* pemFound;
	HRESULT hResult = RIL_E_UNSPECIFIEDGPRSERROR;


    // Look for a "<cr>" to make sure we got the whole number
    if (!MatchStringAnywhere(rszPointer, "\r", szDummy))
    {
        // This isn't a complete error notification -- no need to parse it
        goto Error;
    }

    if (fSigned)
        fRet = ParseIntAsUInt(rszPointer, nCode, rszPointer);
    else
        fRet = ParseUInt(rszPointer, FALSE, nCode, rszPointer);

    if (!fRet)
    {
        // We failed parsing
        ParseJunk(1, rszPointer);
        goto Error;
    }

    emKey.dwCode = nCode;
    pemFound = (ERRORMAP*)bsearch(&emKey, rgemMap, nMapEntries, sizeof(ERRORMAP), CGACTBSCompareErrors);
	if (pemFound)
		{
		hResult = pemFound->hrError;
		} 

    Error:
    return hResult;
}

/*
%CGPCO: <"[DNS1],[DNS2]">,<cid>
*/
HRESULT 
CRilParseSetCGPCO(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, PVOID& pfnParseData)
{
	pBlob = NULL;
	cbBlob = 0;
	DWORD dwNewLength = 0;
	CPdpContext *pCPdpContext = (CPdpContext*)pfnParseData;

	ASSERT(pCPdpContext);

	if ( pCPdpContext )
		{
		if ( szRsp && (TRUE == ParseOK ( TRUE, szRsp, &dwNewLength )))
			{
			RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: CRilParseSetCGPCO OK \r\n"));
			pCPdpContext->SetResultCode ( RIL_RESULT_OK );
			}
		else
			{
			RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: CRilParseSetCGPCO FAIL \r\n"));
			pCPdpContext->SetResultCode ( RIL_E_UNSPECIFIEDGPRSERROR );
			}
	
		if ( pCPdpContext->GetCommandDone() )
			{
			SetEvent( pCPdpContext->GetCommandDone() );
			}
		}
	return S_OK;
}

/*
%CGPCO: <"[DNS1],[DNS2]">,<cid>
*/
HRESULT CRilParseGetCGPCO(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, PVOID& pfnParseData)
{
	LPCSTR szAddr, szAddrEnd;
	int StringLength=0;
	CPdpContext *pCPdpContext = (CPdpContext*)pfnParseData;
	pBlob = NULL;
	cbBlob = 0;
	DWORD DNS1 = 0;
	DWORD DNS2 = 0;
	DWORD Gateway = 0;
	
	ASSERT(pCPdpContext);

	if ( pCPdpContext )
		{
	
		if ((!szRsp)  
			|| (!ParseRspPrefix(szRsp, szRsp))
			|| (!MatchStringBeginning(szRsp, "%CGPCO: ", szRsp))
			||	(!MatchStringBeginning(szRsp,"\"",szRsp)))
			{
			// Rig szAddr so that if we don't find anything, we'll return an empty string
			szAddr=szAddrEnd="\r\n";
			RETAILMSG(MSG_ON, (TEXT("RILNDIS CPdpContext: FAILED \r\n")));
			}
		else
			{
			/*	The command begins with the following flavor:
				%CGPCO: "208.54.36.124,54.63.23.56,66.95.23.45",1<cr><lf>
				At this point we should have something like the following:
				208.54.36.124,54.63.23.56,66.95.23.45",1<cr><lf>

				Processing involves:
				- parse up to comma.
				- process IP
				- parse up to comma.
				- process IP
				- parse up to quotes.
				- process IP

			*/
			szAddr = szRsp;
			RETAILMSG(MSG_ON, (TEXT("RILNDIS CPdpContext: szResp = %hs \r\n"),szAddr));
			if ( MatchStringAnywhere(szRsp, ",", szRsp))
				{
				if ( (szRsp - szAddr) > 7 )
					{
					StringLength = szRsp - szAddr;	
					DNS1 = ExtractIPV4Address(StringLength,  szAddr);
					szAddr = szRsp;
					if ( MatchStringAnywhere(szRsp, ",", szRsp))
						{
						if ( (szRsp - szAddr) > 7 )
							{
							StringLength = szRsp - szAddr;	
							DNS2 = ExtractIPV4Address(StringLength,  szAddr);
							szAddr = szRsp;
							if ( MatchStringAnywhere(szRsp, "\"", szRsp))
								{
								if ( (szRsp - szAddr) > 7 )
									{
									StringLength = szRsp - szAddr;	
									Gateway = ExtractIPV4Address(StringLength,	szAddr);
									szAddr = szRsp;
									}
								}
							}
						}
					}
				}

			
	
			// 
			szAddrEnd = strstr(szAddr, "\r\n");
			if (szAddrEnd)
				{
				szRsp = szAddrEnd;
				}
			}
	

		pCPdpContext->InternalIPV4DNS( DNS1, DNS2, Gateway );
		pCPdpContext->SetResultCode ( RIL_RESULT_OK );
	
		if ( pCPdpContext->GetCommandDone() )
			{
			SetEvent( pCPdpContext->GetCommandDone() );
			}
		}

	return S_OK;
}

HRESULT 
CRilParseDATA(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, PVOID& pfnParseData)
{
	pBlob = NULL;
	cbBlob = 0;
#if defined(ENFORA_DRIVER)
	DWORD dwNewLength = 0;
	CPdpContext *pCPdpContext = (CPdpContext*)pfnParseData;

	ASSERT(pCPdpContext);

	if ( pCPdpContext )
		{
		if ( szRsp && (TRUE == ParseOK ( TRUE, szRsp, &dwNewLength )))
			{
			RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: CRilParseDATA OK \r\n"));
			pCPdpContext->SetResultCode ( RIL_RESULT_OK );
			}
		else
			{
			RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: CRilParseDATA FAIL \r\n"));
			pCPdpContext->SetResultCode ( RIL_E_UNSPECIFIEDGPRSERROR );
			}

		if ( pCPdpContext->GetCommandDone() )
			{
			SetEvent( pCPdpContext->GetCommandDone() );
			}
		}
#endif
	return S_OK;
}

HRESULT 
CRilParseCGDATA(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, PVOID& pfnParseData)
{
	pBlob = NULL;
	cbBlob = 0;
	DWORD dwNewLength = 0;
	CPdpContext *pCPdpContext = (CPdpContext*)pfnParseData;

	ASSERT(pCPdpContext);

	if ( pCPdpContext )
		{
		if ( szRsp && (TRUE == ParseOK ( FALSE, szRsp, &dwNewLength )))
			{
			RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: CRilParseCGDATA OK \r\n"));
			pCPdpContext->SetResultCode ( RIL_RESULT_OK );
			}
		else
			{
			RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: CRilParseCGDATA FAIL \r\n"));
			pCPdpContext->SetResultCode ( RIL_E_UNSPECIFIEDGPRSERROR );
			}
	
		if ( pCPdpContext->GetCommandDone() )
			{
			SetEvent( pCPdpContext->GetCommandDone() );
			}
		}
	return S_OK;
}

static const ERRORMAP g_rgemGPRSCGACTCMEErrors[] =
{    
	{ RIL_E_PHONEFAILURE,      										100},
    { RIL_E_ILLEGALMS,                                              103},
    { RIL_E_ILLEGALME,                                              106},
    { RIL_E_GPRSSERVICENOTALLOWED,                                  107},
    { RIL_E_PLMNNOTALLOWED,                                         111},
    { RIL_E_LOCATIONAREANOTALLOWED,                                 112},
    { RIL_E_ROAMINGNOTALLOWEDINTHISLOCATIONAREA,                    113},
    { RIL_E_SERVICEOPTIONNOTSUPPORTED,                              132},
    { RIL_E_REQUESTEDSERVICEOPTIONNOTSUBSCRIBED,                    133},
    { RIL_E_SERVICEOPTIONTEMPORARILYOUTOFORDER,                     134},
    { RIL_E_UNSPECIFIEDGPRSERROR,                                   148},
    { RIL_E_PDPAUTHENTICATIONFAILURE,                               149},
    { RIL_E_INVALIDMOBILECLASS,                                     150},
 	{ RIL_E_TIMEDOUT,          										512},
};

#define NUM_CGACTGPRSCMEERRORS       (sizeof(g_rgemGPRSCGACTCMEErrors) / sizeof(ERRORMAP))

HRESULT 
CRilParseCGACT(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, PVOID& pfnParseData)
{
	pBlob = NULL;
	cbBlob = 0;
	DWORD dwNewLength = 0;
	CPdpContext *pCPdpContext = (CPdpContext*)pfnParseData;

	ASSERT(pCPdpContext);

	if ( pCPdpContext )
		{
		if ( szRsp && (ACTIVATION_STATE_DEACTIVATE == pCPdpContext->GetActivationState()) )
			{
			if ( MatchStringBeginning(szRsp, "+CGEV: ME DEACT", szRsp))
				{
				pCPdpContext->SetResultCode ( RIL_RESULT_OK );
				}
			else
				{
				pCPdpContext->SetResultCode ( RIL_E_UNSPECIFIEDGPRSERROR );
				}
			}
		else
			{
			if ( szRsp && (TRUE == ParseOK ( TRUE, szRsp, &dwNewLength )))
				{
				RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: CRilParseCGACT OK \r\n"));
				pCPdpContext->SetResultCode ( RIL_RESULT_OK );
				}
			else
				{
				UINT nCode;
				HRESULT hResult = RIL_E_UNSPECIFIEDGPRSERROR;
				RETAILMSG(MSG_ON, (L"RILNDIS CPdpContext: CRilParseCGACT FAIL \r\n"));
				if ( szRsp )
					{
					if (MatchStringAnywhere(szRsp, "+CME ERROR: ", szRsp))
						{
						hResult = ParseExtError(szRsp, g_rgemGPRSCGACTCMEErrors, NUM_CGACTGPRSCMEERRORS, nCode, FALSE);
						}
					}
				pCPdpContext->SetResultCode ( hResult );
				}
			}
	
		if ( pCPdpContext->GetCommandDone() )
			{
			SetEvent( pCPdpContext->GetCommandDone() );
			}
		}
	return S_OK;
}


/*
+CGPADDR: <cid>,<PDP_addr>
[<CR><LF>+CGPADDR: <cid>,<PDP_addr>
[...]]
*/
HRESULT CRilParseGetGPRSAddress(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, PVOID& pfnParseData)
{
	UINT nValue;
	LPCSTR szAddr, szAddrEnd;
	int StringLength=0;
	CPdpContext *pCPdpContext = (CPdpContext*)pfnParseData;
	pBlob = NULL;
	cbBlob = 0;
	DWORD IpAddress = 0;
	
	ASSERT(pCPdpContext);

	if ( pCPdpContext )
		{
		// Note: The pdp address is optional, so ignore it if it's not there and return an empty string
		// Also, ericsson doesn't even give us the cid, so ignore that if it's not present also
	
		if ((!szRsp)  
		   || (!ParseRspPrefix(szRsp, szRsp))
		   || (!MatchStringBeginning(szRsp, "+CGPADDR: ", szRsp))
		   || (!ParseUInt(szRsp, TRUE, nValue, szRsp))
		   || (!MatchStringBeginning(szRsp, ",", szRsp)) 
			|| (!MatchStringBeginning(szRsp,"\"",szRsp)))
			{
			// Rig szAddr so that if we don't find anything, we'll return an empty string
			szAddr=szAddrEnd="\r\n";
			}
		else
			{
			szAddr = szRsp;
			/* We have moved past the first quote. Now, find the second quote */

			if ( MatchStringAnywhere(szRsp, "\"", szRsp))
				{
				/* If we found the quote, szRsp now points to one character beyond */
				StringLength = szRsp - szAddr;
				if ( StringLength > 0 )
					{
					IpAddress = ExtractIPV4Address ( StringLength, szAddr );
					}
				}
			}
	
		// Parse ,<pd1>[,...[,pdN]]
		szAddrEnd = strstr(szAddr, "\r\n");
		if (szAddrEnd)
			{
			szRsp = szAddrEnd;
			}
		
		if (IpAddress)
			{
			pCPdpContext->InternalIPV4GPRSAddress( IpAddress );
			pCPdpContext->SetResultCode ( RIL_RESULT_OK );
			}
		else
			{
			pCPdpContext->SetResultCode ( RIL_RESULT_ERROR );
			}
	
		if ( pCPdpContext->GetCommandDone() )
			{
			SetEvent( pCPdpContext->GetCommandDone() );
			}
		}

	return S_OK;
}
#endif 
